├── .gitignore ├── Resources ├── bar-chart-screenshot.png ├── bar-chart-screenshot-2.png └── Package.swift ├── Examples ├── BarChartExample-iOS │ ├── BarChartExample-iOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ ├── Info.plist │ │ ├── AppDelegate.swift │ │ ├── SceneDelegate.swift │ │ └── ContentView.swift │ └── BarChartExample-iOS.xcodeproj │ │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── BarChartExample-tvOS │ ├── BarChartExample-tvOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── App Icon & Top Shelf Image.brandassets │ │ │ │ ├── App Icon.imagestack │ │ │ │ ├── Back.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Front.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Middle.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ │ ├── App Icon - App Store.imagestack │ │ │ │ ├── Back.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Front.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Middle.imagestacklayer │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Content.imageset │ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ │ ├── Top Shelf Image.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Top Shelf Image Wide.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ ├── AppDelegate.swift │ │ └── ContentView.swift │ └── BarChartExample-tvOS.xcodeproj │ │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── project.pbxproj ├── BarChartExample-macOS │ ├── BarChartExample-macOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ ├── BarChartExample_macOS.entitlements │ │ ├── Info.plist │ │ ├── AppDelegate.swift │ │ └── ContentView.swift │ └── BarChartExample-macOS.xcodeproj │ │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── project.pbxproj ├── BarChartExample-watchOS │ ├── BarChartExample-watchOS WatchKit App │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ └── Interface.storyboard │ │ └── Info.plist │ ├── BarChartExample-watchOS WatchKit Extension │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── Complication.complicationset │ │ │ │ ├── Circular.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Modular.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Extra Large.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Graphic Bezel.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Graphic Corner.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Utilitarian.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Graphic Circular.imageset │ │ │ │ └── Contents.json │ │ │ │ ├── Graphic Large Rectangular.imageset │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── HostingController.swift │ │ ├── ContentView.swift │ │ └── ExtensionDelegate.swift │ └── BarChartExample-watchOS.xcodeproj │ │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── SelectableBarChartExample-iOS │ ├── SelectableBarChartExample-iOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ ├── Info.plist │ │ ├── MiniSelectionIndicator.swift │ │ ├── SelectionLine.swift │ │ ├── AppDelegate.swift │ │ ├── SelectionIndicator.swift │ │ ├── SceneDelegate.swift │ │ └── ContentView.swift │ └── SelectableBarChartExample-iOS.xcodeproj │ │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Package.swift ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── BarChart │ ├── GradientColor.swift │ ├── Extension+Decimals.swift │ ├── ChartData.swift │ ├── BarChartCell.swift │ ├── Extension+StringSize.swift │ ├── ChartConfiguration.swift │ ├── Extension+ViewModifiers.swift │ ├── AxisBase.swift │ ├── XAxisLayout.swift │ ├── BarShape.swift │ ├── XAxis.swift │ ├── YAxis.swift │ ├── BarChartView.swift │ ├── BarChartCollectionView.swift │ ├── YAxisScaler.swift │ └── CoordinateSystemView.swift └── Tests └── BarChartTests ├── XAxisTests.swift └── TestData.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | .swiftpm -------------------------------------------------------------------------------- /Resources/bar-chart-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romanbaitaliuk/BarChart/HEAD/Resources/bar-chart-screenshot.png -------------------------------------------------------------------------------- /Resources/bar-chart-screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romanbaitaliuk/BarChart/HEAD/Resources/bar-chart-screenshot-2.png -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit App/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 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: "Examples", 8 | products: [], 9 | targets: [] 10 | ) 11 | -------------------------------------------------------------------------------- /Resources/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 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: "Resources", 8 | products: [], 9 | targets: [] 10 | ) 11 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | } 11 | ], 12 | "info" : { 13 | "author" : "xcode", 14 | "version" : 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | } 11 | ], 12 | "info" : { 13 | "author" : "xcode", 14 | "version" : 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | } 11 | ], 12 | "info" : { 13 | "author" : "xcode", 14 | "version" : 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS/BarChartExample_macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | }, 6 | "layers" : [ 7 | { 8 | "filename" : "Front.imagestacklayer" 9 | }, 10 | { 11 | "filename" : "Middle.imagestacklayer" 12 | }, 13 | { 14 | "filename" : "Back.imagestacklayer" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | }, 6 | "layers" : [ 7 | { 8 | "filename" : "Front.imagestacklayer" 9 | }, 10 | { 11 | "filename" : "Middle.imagestacklayer" 12 | }, 13 | { 14 | "filename" : "Back.imagestacklayer" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "tv-marketing", 13 | "scale" : "1x" 14 | }, 15 | { 16 | "idiom" : "tv-marketing", 17 | "scale" : "2x" 18 | } 19 | ], 20 | "info" : { 21 | "author" : "xcode", 22 | "version" : 1 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "tv", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "tv", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "tv-marketing", 13 | "scale" : "1x" 14 | }, 15 | { 16 | "idiom" : "tv-marketing", 17 | "scale" : "2x" 18 | } 19 | ], 20 | "info" : { 21 | "author" : "xcode", 22 | "version" : 1 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">161" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "scale" : "2x", 16 | "screen-width" : ">145" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "scale" : "2x", 21 | "screen-width" : ">183" 22 | } 23 | ], 24 | "info" : { 25 | "author" : "xcode", 26 | "version" : 1 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "filename" : "App Icon - App Store.imagestack", 5 | "idiom" : "tv", 6 | "role" : "primary-app-icon", 7 | "size" : "1280x768" 8 | }, 9 | { 10 | "filename" : "App Icon.imagestack", 11 | "idiom" : "tv", 12 | "role" : "primary-app-icon", 13 | "size" : "400x240" 14 | }, 15 | { 16 | "filename" : "Top Shelf Image Wide.imageset", 17 | "idiom" : "tv", 18 | "role" : "top-shelf-image-wide", 19 | "size" : "2320x720" 20 | }, 21 | { 22 | "filename" : "Top Shelf Image.imageset", 23 | "idiom" : "tv", 24 | "role" : "top-shelf-image", 25 | "size" : "1920x720" 26 | } 27 | ], 28 | "info" : { 29 | "author" : "xcode", 30 | "version" : 1 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit App/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | arm64 28 | 29 | UIUserInterfaceStyle 30 | Automatic 31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | ----------- 3 | 4 | Copyright (c) 2020 Roman Baitaliuk 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | BarChartExample-watchOS WatchKit App 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | UISupportedInterfaceOrientations 24 | 25 | UIInterfaceOrientationPortrait 26 | UIInterfaceOrientationPortraitUpsideDown 27 | 28 | WKWatchKitApp 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 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: "BarChart", 8 | platforms: [ 9 | .iOS(.v13), .macOS(.v10_15), .watchOS(.v6), .tvOS(.v13) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .library( 14 | name: "BarChart", 15 | targets: ["BarChart"]), 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 which this package depends on. 24 | .target( 25 | name: "BarChart", 26 | dependencies: []), 27 | .testTarget( 28 | name: "BarChartTests", 29 | dependencies: ["BarChart"]), 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Assets.xcassets/Complication.complicationset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "filename" : "Circular.imageset", 5 | "idiom" : "watch", 6 | "role" : "circular" 7 | }, 8 | { 9 | "filename" : "Extra Large.imageset", 10 | "idiom" : "watch", 11 | "role" : "extra-large" 12 | }, 13 | { 14 | "filename" : "Graphic Bezel.imageset", 15 | "idiom" : "watch", 16 | "role" : "graphic-bezel" 17 | }, 18 | { 19 | "filename" : "Graphic Circular.imageset", 20 | "idiom" : "watch", 21 | "role" : "graphic-circular" 22 | }, 23 | { 24 | "filename" : "Graphic Corner.imageset", 25 | "idiom" : "watch", 26 | "role" : "graphic-corner" 27 | }, 28 | { 29 | "filename" : "Graphic Large Rectangular.imageset", 30 | "idiom" : "watch", 31 | "role" : "graphic-large-rectangular" 32 | }, 33 | { 34 | "filename" : "Modular.imageset", 35 | "idiom" : "watch", 36 | "role" : "modular" 37 | }, 38 | { 39 | "filename" : "Utilitarian.imageset", 40 | "idiom" : "watch", 41 | "role" : "utilitarian" 42 | } 43 | ], 44 | "info" : { 45 | "author" : "xcode", 46 | "version" : 1 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2020 Roman Baitaliuk. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | NSSupportsAutomaticTermination 32 | 33 | NSSupportsSuddenTermination 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SwiftUI BarChart 2 | Lightweight and easy to use SwiftUI chart library for all Apple platforms 3 | 4 |
5 | 6 |
7 | 8 |
9 | 10 |
11 | 12 | ## Features 13 | 14 | - Scaling on both axes 15 | - Fully customizable axes (labels font, color, dashed lines) 16 | - Custom gradient bars 17 | - Reactive chart configuration 18 | - Bar selection API 19 | - **[WIP]** horizontal scrolling 20 | 21 | ## Requirements 22 | 23 | - iOS 13+ / macOS 10.15+ / watchOS 6+ / tvOS 13+ 24 | - Xcode 11.0+ 25 | - Swift 5+ 26 | 27 | ## Installation 28 | 29 | ### Swift Package Manager 30 | 31 | Add this swift package to your project 32 | ``` 33 | https://github.com/dawigr/BarChart.git 34 | ``` 35 | 36 | ## Usage 37 | 38 | [See Wiki](https://github.com/dawigr/BarChart/wiki) for usage details 39 | 40 | ## Help 41 | 42 | If you like the library, you could: 43 | - Contribute code, issues and pull requests 44 | - Let other people know about it 45 | - [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7D5E9VK2WYZUY) any amount to accelaerate new feature development 46 | 47 | ## License 48 | 49 | BarChart is released under the MIT license. [See LICENSE](https://github.com/dawigr/BarChart/blob/master/LICENSE) for details 50 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | BarChartExample-watchOS WatchKit Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | WKAppBundleIdentifier 28 | com.ByteKit.BarChartExample-watchOS.watchkitapp 29 | 30 | NSExtensionPointIdentifier 31 | com.apple.watchkit 32 | 33 | WKExtensionDelegateClassName 34 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 35 | WKWatchOnly 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/HostingController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HostingController.swift 3 | // BarChartExample-watchOS WatchKit Extension 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import WatchKit 28 | import Foundation 29 | import SwiftUI 30 | 31 | class HostingController: WKHostingController { 32 | override var body: ContentView { 33 | return ContentView() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/BarChart/GradientColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradientColor.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | public struct GradientColor { 30 | public let start: Color 31 | public let end: Color 32 | 33 | public init(start: Color, end: Color) { 34 | self.start = start 35 | self.end = end 36 | } 37 | 38 | public func gradient() -> Gradient { 39 | return Gradient(colors: [start, end]) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/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 | -------------------------------------------------------------------------------- /Sources/BarChart/Extension+Decimals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+Decimals.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | extension Double { 30 | func decimalsCount() -> Int { 31 | if self == Double(Int(self)) { 32 | return 0 33 | } 34 | 35 | let integerString = String(Int(self)) 36 | let doubleString = String(Double(self)) 37 | let decimalCount = doubleString.count - integerString.count - 1 38 | 39 | return decimalCount 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/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 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/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 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit App/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "role" : "notificationCenter", 6 | "scale" : "2x", 7 | "size" : "24x24", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "idiom" : "watch", 12 | "role" : "notificationCenter", 13 | "scale" : "2x", 14 | "size" : "27.5x27.5", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "idiom" : "watch", 19 | "role" : "companionSettings", 20 | "scale" : "2x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "watch", 25 | "role" : "companionSettings", 26 | "scale" : "3x", 27 | "size" : "29x29" 28 | }, 29 | { 30 | "idiom" : "watch", 31 | "role" : "appLauncher", 32 | "scale" : "2x", 33 | "size" : "40x40", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "idiom" : "watch", 38 | "role" : "appLauncher", 39 | "scale" : "2x", 40 | "size" : "44x44", 41 | "subtype" : "40mm" 42 | }, 43 | { 44 | "idiom" : "watch", 45 | "role" : "appLauncher", 46 | "scale" : "2x", 47 | "size" : "50x50", 48 | "subtype" : "44mm" 49 | }, 50 | { 51 | "idiom" : "watch", 52 | "role" : "quickLook", 53 | "scale" : "2x", 54 | "size" : "86x86", 55 | "subtype" : "38mm" 56 | }, 57 | { 58 | "idiom" : "watch", 59 | "role" : "quickLook", 60 | "scale" : "2x", 61 | "size" : "98x98", 62 | "subtype" : "42mm" 63 | }, 64 | { 65 | "idiom" : "watch", 66 | "role" : "quickLook", 67 | "scale" : "2x", 68 | "size" : "108x108", 69 | "subtype" : "44mm" 70 | }, 71 | { 72 | "idiom" : "watch-marketing", 73 | "scale" : "1x", 74 | "size" : "1024x1024" 75 | } 76 | ], 77 | "info" : { 78 | "author" : "xcode", 79 | "version" : 1 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/BarChart/ChartData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartData.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | public struct ChartDataEntry: Identifiable, Equatable { 30 | public var id = UUID() 31 | public var x: String 32 | public var y: Double 33 | 34 | public init(x: String, y: Double) { 35 | self.x = x 36 | self.y = y 37 | } 38 | } 39 | 40 | public struct ChartData { 41 | public var entries: [ChartDataEntry] = [] 42 | public var gradientColor: GradientColor? 43 | public var color: Color = .red { 44 | didSet { 45 | self.gradientColor = nil 46 | } 47 | } 48 | 49 | var yValues: [Double] { 50 | return self.entries.map { $0.y } 51 | } 52 | 53 | var xValues: [String] { 54 | return self.entries.map { $0.x } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/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 | -------------------------------------------------------------------------------- /Sources/BarChart/BarChartCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarChartCell.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | struct BarChartCell: View { 30 | let width, height, cornerRadius: CGFloat 31 | let gradient: Gradient? 32 | let color: Color 33 | 34 | var body: some View { 35 | Group { 36 | if self.gradient != nil { 37 | BarShape(cornerRadius: self.cornerRadius, 38 | corners: RoundedCorner.allCases) 39 | .fill(LinearGradient(gradient: self.gradient!, 40 | startPoint: .bottom, 41 | endPoint: .top)) 42 | } else { 43 | BarShape(cornerRadius: self.cornerRadius, 44 | corners: RoundedCorner.allCases) 45 | .fill(self.color) 46 | } 47 | } 48 | .frame(width: self.width, height: self.height) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/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 | -------------------------------------------------------------------------------- /Sources/BarChart/Extension+StringSize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+StringSize.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | extension String { 30 | func width(ctFont: CTFont) -> CGFloat { 31 | return self.size(ctFont: ctFont).width 32 | } 33 | 34 | func height(ctFont: CTFont) -> CGFloat { 35 | return self.size(ctFont: ctFont).height 36 | } 37 | 38 | func size(ctFont: CTFont) -> CGSize { 39 | let attributes = [NSAttributedString.Key.init(kCTFontAttributeName as String): ctFont] 40 | let attString = NSAttributedString(string: self == "" ? " " : self, attributes: attributes) 41 | let framesetter = CTFramesetterCreateWithAttributedString(attString) 42 | let range = CFRange(location: 0, length: self.count) 43 | let size = CGSize(width: 10000, height: 10000) 44 | let frame = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, range, nil, size, nil) 45 | return frame 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/BarChart/ChartConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartConfiguration.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | import Combine 29 | 30 | public class ChartConfiguration: ObservableObject { 31 | @Published public var data = ChartData() 32 | public var xAxis = XAxisReference() 33 | public var yAxis = YAxisReference() 34 | 35 | private var xAxisCancellable: AnyCancellable? 36 | private var yAxisCancellable: AnyCancellable? 37 | 38 | static let defaultLabelsCTFont = CTFontCreateWithName(("SFProText-Regular" as CFString), 12, nil) 39 | @Published public var labelsCTFont: CTFont = ChartConfiguration.defaultLabelsCTFont 40 | 41 | public init() { 42 | self.xAxisCancellable = self.xAxis.objectWillChange.sink(receiveValue: { _ in 43 | self.objectWillChange.send() 44 | }) 45 | 46 | self.yAxisCancellable = self.yAxis.objectWillChange.sink(receiveValue: { _ in 47 | self.objectWillChange.send() 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/MiniSelectionIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiniSelectionIndicator.swift 3 | // SelectableBarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | import BarChart 29 | 30 | struct MiniSelectionIndicator: View { 31 | let entry: ChartDataEntry? 32 | let location: CGPoint? 33 | 34 | let height: CGFloat = 30 35 | let width: CGFloat = 40 36 | let spaceFromBar: CGFloat = 5 37 | let color: Color = Color(red: 230/255, green: 230/255, blue: 230/255) 38 | 39 | var body: some View { 40 | Group { 41 | if location != nil && self.entry != nil { 42 | ZStack { 43 | RoundedRectangle(cornerRadius: 3.0) 44 | .foregroundColor(self.color) 45 | Text("\(Int(self.entry!.y))").font(.system(size: 12)).fontWeight(.bold) 46 | } 47 | .zIndex(1) 48 | .frame(width: self.width, height: self.height) 49 | .offset(x: self.location!.x - self.width / 2, y: self.location!.y - (self.height + self.spaceFromBar)) 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BarChartExample-macOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import Cocoa 28 | import SwiftUI 29 | 30 | @NSApplicationMain 31 | class AppDelegate: NSObject, NSApplicationDelegate { 32 | 33 | var window: NSWindow! 34 | 35 | 36 | func applicationDidFinishLaunching(_ aNotification: Notification) { 37 | // Create the SwiftUI view that provides the window contents. 38 | let contentView = ContentView() 39 | 40 | // Create the window and set the content view. 41 | window = NSWindow( 42 | contentRect: NSRect(x: 0, y: 0, width: 600, height: 500), 43 | styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView], 44 | backing: .buffered, defer: false) 45 | window.center() 46 | window.setFrameAutosaveName("Main Window") 47 | window.contentView = NSHostingView(rootView: contentView) 48 | window.makeKeyAndOrderFront(nil) 49 | } 50 | 51 | func applicationWillTerminate(_ aNotification: Notification) { 52 | // Insert code here to tear down your application 53 | } 54 | 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Sources/BarChart/Extension+ViewModifiers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extension+ViewModifiers.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | public typealias BarChartView = SelectableBarChartView 30 | 31 | #if !os(tvOS) 32 | public extension SelectableBarChartView { 33 | func onBarSelection(_ callback: @escaping (ChartDataEntry, CGPoint) -> ()) -> Self { 34 | SelectableBarChartView(config: self.config, selectionCallback: callback) 35 | } 36 | 37 | func selectionView(@ViewBuilder view: () -> SelectionView) -> Self { 38 | SelectableBarChartView(config: self.config, selectionCallback: self.selectionCallback, selectionView: view()) 39 | } 40 | } 41 | #endif 42 | 43 | extension View { 44 | func onSelect(callback: @escaping () -> ()) -> some View { 45 | return self.modifier(Selectable(callback: callback)) 46 | } 47 | } 48 | 49 | struct Selectable: ViewModifier { 50 | let callback: () -> () 51 | 52 | func body(content: Content) -> some View { 53 | Group { 54 | #if !os(tvOS) 55 | content 56 | .onTapGesture { 57 | self.callback() 58 | } 59 | #else 60 | content 61 | #endif 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/SelectionLine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionLine.swift 3 | // SelectableBarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | struct SelectionLine: View { 30 | let location: CGPoint? 31 | let height: CGFloat 32 | let color = Color(red: 100/255, green: 100/255, blue: 100/255) 33 | 34 | var body: some View { 35 | Group { 36 | if location != nil { 37 | self.centreLine() 38 | .stroke(lineWidth: 2) 39 | .offset(x: self.location!.x) 40 | .foregroundColor(self.color) 41 | /* '.id(UUID())' will prevent view from slide animation. 42 | Because this view is a child view and passed to 'BarChartView' parent, parent might already has animation. 43 | So, If you want to disable it, just call '.animation(nil)' instead of '.id(UUID())' */ 44 | .id(UUID()) 45 | } 46 | } 47 | } 48 | 49 | func centreLine() -> Path { 50 | var path = Path() 51 | let p1 = CGPoint(x: 0, y: 0) 52 | let p2 = CGPoint(x: 0, y: self.height) 53 | path.move(to: p1) 54 | path.addLine(to: p2) 55 | return path 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/BarChartTests/XAxisTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XAxisTests.swift 3 | // BarChartTests 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import XCTest 28 | @testable import BarChart 29 | 30 | class XAxisTests: XCTestCase { 31 | 32 | func testTicksInterval1() { 33 | let xAxis = XAxis(frameWidth: 300, data: TestData.generate(with: TestData.Values.Positive.Small.values4)) 34 | let expectedLabels = ["0", "1", "2", "3", "4", "5", "6"] 35 | XCTAssert(xAxis.formattedLabels() == expectedLabels) 36 | } 37 | 38 | func testTicksInterval2() { 39 | let ref = XAxisReference() 40 | ref.ticksInterval = 2 41 | let xAxis = XAxis(frameWidth: 300, 42 | data: TestData.generate(with: TestData.Values.Positive.Small.values4), 43 | ref: ref) 44 | let expectedLabels = ["0", "2", "4", "6"] 45 | XCTAssert(xAxis.formattedLabels() == expectedLabels) 46 | } 47 | 48 | func testTicksInterval3() { 49 | let ref = XAxisReference() 50 | ref.ticksInterval = 0 51 | let xAxis = XAxis(frameWidth: 300, 52 | data: TestData.generate(with: TestData.Values.Positive.Small.values4), 53 | ref: ref) 54 | let expectedLabels = ["0", "1", "2", "3", "4", "5", "6"] 55 | XCTAssert(xAxis.formattedLabels() == expectedLabels) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/BarChart/AxisBase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxisBase.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | public class AxisBase: ObservableObject { 30 | @Published public var labelsColor: Color = .black 31 | @Published public var ticksColor: Color = .black 32 | @Published public var ticksStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [5, 10]) 33 | } 34 | 35 | public class XAxisReference: AxisBase { 36 | /// Horizontal interval between the bars 37 | @Published public var ticksInterval: Int? { 38 | didSet { 39 | self.validateTicksInterval() 40 | } 41 | } 42 | 43 | private func validateTicksInterval() { 44 | if let newValue = self.ticksInterval, newValue < 1 { 45 | self.ticksInterval = nil 46 | } 47 | } 48 | } 49 | 50 | public class YAxisReference: AxisBase { 51 | /// Minimum spacing between the ticks in pixels 52 | @Published public var minTicksSpacing: CGFloat = 40.0 { 53 | didSet { 54 | self.validateMinTicksSpacing() 55 | } 56 | } 57 | 58 | @Published public var formatter: ((Double, Int) -> String) = { 59 | return { return String(format: "%.\($1)f", $0) } 60 | }() 61 | 62 | private func validateMinTicksSpacing() { 63 | if minTicksSpacing <= 0 { 64 | self.minTicksSpacing = 40.0 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/BarChart/XAxisLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XAxisLayout.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | struct XAxisLayout { 30 | private let frameWidth: CGFloat 31 | private let dataCount: Int 32 | 33 | private(set) var spacing: CGFloat? 34 | private(set) var barWidth: CGFloat? 35 | 36 | init(frameWidth: CGFloat, dataCount: Int) { 37 | self.frameWidth = frameWidth 38 | self.dataCount = dataCount 39 | self.calculate() 40 | } 41 | 42 | func barCentre(at index: Int) -> CGFloat? { 43 | guard let barWidth = self.barWidth, 44 | let spacing = self.spacing else { return nil } 45 | if self.dataCount == 1 { 46 | return spacing + barWidth / 2 47 | } else { 48 | let centre = barWidth / 2 49 | return barWidth * CGFloat(index + 1) + spacing * CGFloat(index) - centre 50 | } 51 | } 52 | 53 | private mutating func calculate() { 54 | guard self.dataCount != 0 else { return } 55 | 56 | let barWidth = self.frameWidth / (CGFloat(self.dataCount) * 1.5) 57 | self.barWidth = barWidth 58 | 59 | if self.dataCount == 1 { 60 | self.spacing = (self.frameWidth - barWidth) / 2 61 | } else { 62 | self.spacing = self.frameWidth / CGFloat((self.dataCount - 1) * 3) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | @UIApplicationMain 30 | class AppDelegate: UIResponder, UIApplicationDelegate { 31 | 32 | 33 | 34 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 35 | // Override point for customization after application launch. 36 | return true 37 | } 38 | 39 | // MARK: UISceneSession Lifecycle 40 | 41 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 42 | // Called when a new scene session is being created. 43 | // Use this method to select a configuration to create the new scene with. 44 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 45 | } 46 | 47 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 48 | // Called when the user discards a scene session. 49 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 50 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 51 | } 52 | 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SelectableBarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import UIKit 28 | 29 | @UIApplicationMain 30 | class AppDelegate: UIResponder, UIApplicationDelegate { 31 | 32 | 33 | 34 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 35 | // Override point for customization after application launch. 36 | return true 37 | } 38 | 39 | // MARK: UISceneSession Lifecycle 40 | 41 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 42 | // Called when a new scene session is being created. 43 | // Use this method to select a configuration to create the new scene with. 44 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 45 | } 46 | 47 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 48 | // Called when the user discards a scene session. 49 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 50 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 51 | } 52 | 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // BarChartExample-watchOS WatchKit Extension 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | import BarChart 29 | 30 | struct ContentView: View { 31 | 32 | // MARK: - Chart Properties 33 | let config = ChartConfiguration() 34 | 35 | var body: some View { 36 | GeometryReader { geometry in 37 | BarChartView(config: self.config) 38 | .onAppear() { 39 | let font = CTFontCreateWithName(("SFProText-Regular" as CFString), 8, nil) 40 | self.config.data.entries = self.randomEntries() 41 | self.config.labelsCTFont = font 42 | self.config.xAxis.labelsColor = .white 43 | self.config.xAxis.ticksColor = .white 44 | self.config.yAxis.labelsColor = .white 45 | self.config.yAxis.ticksColor = .white 46 | self.config.yAxis.minTicksSpacing = 20.0 47 | } 48 | .animation(Animation.easeInOut.delay(1.0)) 49 | .frame(height: geometry.size.height) 50 | } 51 | } 52 | 53 | // MARK: - Random Helpers 54 | 55 | func randomEntries() -> [ChartDataEntry] { 56 | var entries = [ChartDataEntry]() 57 | for data in 0..<8 { 58 | let randomDouble = Double.random(in: 0...10) 59 | let newEntry = ChartDataEntry(x: "\(data)", y: randomDouble) 60 | entries.append(newEntry) 61 | } 62 | return entries 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/SelectionIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionIndicator.swift 3 | // SelectableBarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | import BarChart 29 | 30 | struct SelectionIndicator: View { 31 | let entry: ChartDataEntry 32 | let location: CGFloat 33 | let infoRectangleColor: Color 34 | let infoRectangleWidth: CGFloat = 70 35 | 36 | var body: some View { 37 | GeometryReader { proxy in 38 | ZStack { 39 | RoundedRectangle(cornerRadius: 3.0) 40 | .foregroundColor(self.infoRectangleColor) 41 | VStack(alignment: .leading) { 42 | HStack(alignment: .bottom, spacing: 2) { 43 | Text("\(Int(self.entry.y))").font(.headline).fontWeight(.bold) 44 | Text("b").font(.footnote) 45 | .foregroundColor(.gray).fontWeight(.bold) 46 | } 47 | Text(self.entry.x) 48 | .font(.footnote).foregroundColor(.gray).fontWeight(.bold) 49 | } 50 | } 51 | .frame(width: self.infoRectangleWidth) 52 | .offset(x: self.positionX(proxy, location: self.location)) 53 | // '.id(UUID())' will prevent view from slide animation. 54 | .id(UUID()) 55 | } 56 | } 57 | 58 | func positionX(_ proxy: GeometryProxy, location: CGFloat) -> CGFloat { 59 | let selectorCentre = self.infoRectangleWidth / 2 60 | let startX = location - selectorCentre 61 | if startX < 0 { 62 | return 0 63 | } else if startX + self.infoRectangleWidth > proxy.size.width { 64 | return proxy.size.width - self.infoRectangleWidth 65 | } else { 66 | return startX 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/BarChart/BarShape.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarShape.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | enum RoundedCorner: CaseIterable { 30 | case topLeft, topRight, bottomRight, bottomLeft 31 | } 32 | 33 | struct BarShape: Shape { 34 | let cornerRadius: CGFloat 35 | let corners: [RoundedCorner] 36 | 37 | func radius(for corner: RoundedCorner) -> CGFloat { 38 | if self.corners.contains(corner) { 39 | return self.cornerRadius 40 | } 41 | return 0 42 | } 43 | 44 | func path(in rect: CGRect) -> Path { 45 | var path = Path() 46 | 47 | let control1 = CGPoint(x: rect.minX, y: rect.maxY) 48 | let control2 = CGPoint(x: rect.maxX, y: rect.maxY) 49 | let control3 = CGPoint(x: rect.maxX, y: rect.minY) 50 | let control4 = CGPoint(x: rect.minX, y: rect.minY) 51 | 52 | let p1 = CGPoint(x: rect.minX, y: rect.minY + self.radius(for: .bottomLeft)) 53 | let p2 = CGPoint(x: rect.minX, y: rect.maxY - self.radius(for: .topLeft)) 54 | let p3 = CGPoint(x: rect.minX + self.radius(for: .topLeft), y: rect.maxY) 55 | let p4 = CGPoint(x: rect.maxX - self.radius(for: .topRight), y: rect.maxY) 56 | let p5 = CGPoint(x: rect.maxX, y: rect.maxY - self.radius(for: .topRight)) 57 | let p6 = CGPoint(x: rect.maxX, y: rect.minY + self.radius(for: .bottomRight)) 58 | let p7 = CGPoint(x: rect.maxX - self.radius(for: .bottomRight), y: rect.minY) 59 | let p8 = CGPoint(x: rect.minX + self.radius(for: .bottomLeft), y: rect.minY) 60 | 61 | path.move(to: p1) 62 | path.addLine(to: p2) 63 | path.addQuadCurve(to: p3, control: control1) 64 | path.addLine(to: p4) 65 | path.addQuadCurve(to: p5, control: control2) 66 | path.addLine(to: p6) 67 | path.addQuadCurve(to: p7, control: control3) 68 | path.addLine(to: p8) 69 | path.addQuadCurve(to: p1, control: control4) 70 | 71 | return path 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BarChartExample-tvOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import UIKit 28 | import SwiftUI 29 | 30 | @UIApplicationMain 31 | class AppDelegate: UIResponder, UIApplicationDelegate { 32 | 33 | var window: UIWindow? 34 | 35 | 36 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 37 | 38 | // Create the SwiftUI view that provides the window contents. 39 | let contentView = ContentView() 40 | 41 | // Use a UIHostingController as window root view controller. 42 | let window = UIWindow(frame: UIScreen.main.bounds) 43 | window.rootViewController = UIHostingController(rootView: contentView) 44 | self.window = window 45 | window.makeKeyAndVisible() 46 | return true 47 | } 48 | 49 | func applicationWillResignActive(_ application: UIApplication) { 50 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 51 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 52 | } 53 | 54 | func applicationDidEnterBackground(_ application: UIApplication) { 55 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 56 | } 57 | 58 | func applicationWillEnterForeground(_ application: UIApplication) { 59 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 60 | } 61 | 62 | func applicationDidBecomeActive(_ application: UIApplication) { 63 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 64 | } 65 | 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /Sources/BarChart/XAxis.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XAxis.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | struct XAxis: Identifiable { 30 | let id = UUID() 31 | let data: [ChartDataEntry] 32 | let frameWidth: CGFloat 33 | let ref: XAxisReference 34 | let labelsCTFont: CTFont 35 | 36 | var layout: XAxisLayout { 37 | XAxisLayout(frameWidth: self.frameWidth, dataCount: self.data.count) 38 | } 39 | 40 | init(frameWidth: CGFloat = 0, 41 | data: [ChartDataEntry] = [], 42 | ref: XAxisReference = XAxisReference(), 43 | labelsCTFont: CTFont = ChartConfiguration.defaultLabelsCTFont) { 44 | self.labelsCTFont = labelsCTFont 45 | self.frameWidth = frameWidth 46 | self.data = data 47 | self.ref = ref 48 | } 49 | 50 | func chartEntry(at index: Int) -> ChartDataEntry { 51 | return self.labels()[index] 52 | } 53 | 54 | func formattedLabels() -> [String] { 55 | return self.labels().map { $0.x } 56 | } 57 | 58 | private func labels() -> [ChartDataEntry] { 59 | guard !self.data.isEmpty else { return [] } 60 | let totalLabelsWidth = self.data.compactMap { $0.x.width(ctFont: self.labelsCTFont) }.reduce(0, +) 61 | let averageLabelWidth = totalLabelsWidth / CGFloat(data.count) 62 | 63 | guard averageLabelWidth != 0 else { return [] } 64 | let maxLabelsCount = Int((frameWidth / averageLabelWidth)) 65 | 66 | if let interval = self.ref.ticksInterval { 67 | return self.calculateLabels(with: interval, to: maxLabelsCount) 68 | } 69 | 70 | if maxLabelsCount > 0, maxLabelsCount < self.data.count { 71 | return self.calculateLabels(with: 2, to: maxLabelsCount) 72 | } else { 73 | return self.data 74 | } 75 | } 76 | 77 | private func calculateLabels(with interval: Int, 78 | to maxLabelsCount: Int) -> [ChartDataEntry] { 79 | let reversedData = Array(self.data.reversed()) 80 | var finalLabels = [ChartDataEntry]() 81 | for index in stride(from: 0, through: self.data.count - 1, by: interval) { 82 | finalLabels.append(reversedData[index]) 83 | } 84 | if finalLabels.count > maxLabelsCount { 85 | let adj = self.ref.ticksInterval ?? 1 86 | return self.calculateLabels(with: interval + adj, to: maxLabelsCount) 87 | } 88 | return Array(finalLabels.reversed()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/BarChart/YAxis.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YAxisLayout.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | struct YAxis: Identifiable { 30 | let id = UUID() 31 | let data: [Double] 32 | let frameHeight: CGFloat 33 | let ref: YAxisReference 34 | let labelsCTFont: CTFont 35 | 36 | var scaler: YAxisScaler? { 37 | guard let minValue = self.data.min(), 38 | let maxValue = self.data.max() else { 39 | return nil 40 | } 41 | let adjustedMin = minValue > 0 ? 0 : minValue 42 | let adjustedMax = maxValue < 0 ? 0 : maxValue 43 | return YAxisScaler(min: adjustedMin, max: adjustedMax, maxTicks: maxTicks) 44 | } 45 | 46 | var maxLabelWidth: CGFloat { 47 | return self.formattedLabels().map { $0.width(ctFont: self.labelsCTFont) }.max() ?? 0 48 | } 49 | 50 | init(frameHeight: CGFloat = 0, 51 | data: [Double] = [], 52 | ref: YAxisReference = YAxisReference(), 53 | labelsCTFont: CTFont = ChartConfiguration.defaultLabelsCTFont) { 54 | self.labelsCTFont = labelsCTFont 55 | self.frameHeight = frameHeight 56 | self.data = data 57 | self.ref = ref 58 | } 59 | 60 | private var maxTicks: Int { 61 | return Int(self.frameHeight / self.ref.minTicksSpacing) 62 | } 63 | 64 | func formattedLabels() -> [String] { 65 | guard let tickSpacing = self.scaler?.tickSpacing else { return [] } 66 | return self.scaler?.scaledValues().map { self.ref.formatter($0, tickSpacing.decimalsCount()) } ?? [] 67 | } 68 | 69 | func labelValue(at index: Int) -> Double? { 70 | return self.scaler?.scaledValues()[index] 71 | } 72 | 73 | func pixelsRatio() -> CGFloat? { 74 | guard let verticalDistance = self.verticalDistance(), 75 | verticalDistance != 0 else { return nil } 76 | return self.frameHeight / CGFloat(verticalDistance) 77 | } 78 | 79 | func centre() -> CGFloat? { 80 | guard let chartMin = self.scaler?.scaledMin, 81 | let pixelsRatio = self.pixelsRatio() else { return nil } 82 | return CGFloat(chartMin) * pixelsRatio 83 | } 84 | 85 | func normalizedValues() -> [Double] { 86 | guard let verticalDistance = self.verticalDistance(), 87 | verticalDistance != 0 else { return [] } 88 | return self.data.map { $0 / verticalDistance } 89 | } 90 | 91 | private func verticalDistance() -> Double? { 92 | guard let chartMax = self.scaler?.scaledMax, 93 | let chartMin = self.scaler?.scaledMin else { return nil } 94 | return abs(chartMax) + abs(chartMin) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Sources/BarChart/BarChartView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarChartView.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | public struct SelectableBarChartView : View { 30 | @ObservedObject var config: ChartConfiguration 31 | @State var xAxis = XAxis() 32 | @State var yAxis = YAxis() 33 | @State var selectionCallback: ((ChartDataEntry, CGPoint) -> Void)? 34 | var selectionView: SelectionView? 35 | 36 | public init(config: ChartConfiguration) { 37 | self.config = config 38 | } 39 | 40 | init(config: ChartConfiguration, 41 | selectionCallback: ((ChartDataEntry, CGPoint) -> Void)? = nil, 42 | selectionView: SelectionView? = nil) { 43 | self.init(config: config) 44 | self._selectionCallback = State(wrappedValue: selectionCallback) 45 | self.selectionView = selectionView 46 | } 47 | 48 | public var body: some View { 49 | ZStack { 50 | GeometryReader { proxy in 51 | CoordinateSystemView(yAxis: self.yAxis, 52 | xAxis: self.xAxis, 53 | frameSize: proxy.size).id(UUID()) 54 | .onReceive(self.config.objectWillChange) { _ in 55 | self.yAxis = YAxis(frameHeight: self.yAxisHeight(proxy.size.height), 56 | data: self.config.data.yValues, 57 | ref: self.config.yAxis, 58 | labelsCTFont: self.config.labelsCTFont) 59 | self.xAxis = XAxis(frameWidth: proxy.size.width - self.yAxis.maxLabelWidth, 60 | data: self.config.data.entries, 61 | ref: self.config.xAxis, 62 | labelsCTFont: self.config.labelsCTFont) 63 | } 64 | self.selectionView 65 | BarChartCollectionView(yAxis: self.yAxis, 66 | xAxis: self.xAxis, 67 | gradient: self.config.data.gradientColor?.gradient(), 68 | color: self.config.data.color, 69 | selectionCallback: self.$selectionCallback) 70 | } 71 | } 72 | } 73 | 74 | private func yAxisHeight(_ frameHeight: CGFloat) -> CGFloat { 75 | let labelsHeight = String().height(ctFont: self.config.labelsCTFont) 76 | let topPadding = labelsHeight / 2 77 | let bottomPadding = labelsHeight * 1.5 78 | return frameHeight - (topPadding + bottomPadding) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/BarChart/BarChartCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarChartCollection.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | 29 | struct BarChartCollectionView: View { 30 | let yAxis: YAxis 31 | let xAxis: XAxis 32 | let gradient: Gradient? 33 | let color: Color 34 | @Binding var selectionCallback: ((ChartDataEntry, CGPoint) -> Void)? 35 | 36 | var body: some View { 37 | HStack(alignment: .bottom, spacing: self.xAxis.layout.spacing ?? 0) { 38 | if self.xAxis.layout.barWidth != nil { 39 | ForEach(0.. CGPoint { 57 | let x = self.xAxis.layout.barCentre(at: index)! 58 | let value = CGFloat(self.yAxis.normalizedValues()[index]) 59 | let y = self.calculateTopOffset(for: value) 60 | return CGPoint(x: x, y: y) 61 | } 62 | 63 | func offsetY(at index: Int) -> CGFloat { 64 | guard let maxNormalizedValue = self.yAxis.normalizedValues().max() else { return 0 } 65 | let chartNormalisedMax = maxNormalizedValue > 0 ? maxNormalizedValue : 0 66 | let absoluteMax = abs(CGFloat(chartNormalisedMax)) 67 | var offset = self.calculateTopOffset(for: absoluteMax) 68 | let barHeight = self.barHeight(at: index) 69 | // Adding offset for bars with negative normalised value 70 | if barHeight < 0 { 71 | offset -= barHeight 72 | } 73 | return offset 74 | } 75 | 76 | func calculateTopOffset(for value: CGFloat) -> CGFloat { 77 | guard let centre = self.yAxis.centre() else { return 0 } 78 | let maxBarHeight = value * self.yAxis.frameHeight 79 | let topPadding = String().height(ctFont: self.xAxis.labelsCTFont) / 2 80 | return self.yAxis.frameHeight - abs(centre) - maxBarHeight + topPadding 81 | } 82 | 83 | func barHeight(at index: Int) -> CGFloat { 84 | return CGFloat(self.yAxis.normalizedValues()[index]) * self.yAxis.frameHeight 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // BarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import UIKit 28 | import SwiftUI 29 | 30 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 31 | 32 | var window: UIWindow? 33 | 34 | 35 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 36 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 37 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 38 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 39 | 40 | // Create the SwiftUI view that provides the window contents. 41 | let contentView = ContentView() 42 | 43 | // Use a UIHostingController as window root view controller. 44 | if let windowScene = scene as? UIWindowScene { 45 | let window = UIWindow(windowScene: windowScene) 46 | window.rootViewController = UIHostingController(rootView: contentView) 47 | self.window = window 48 | window.makeKeyAndVisible() 49 | } 50 | } 51 | 52 | func sceneDidDisconnect(_ scene: UIScene) { 53 | // Called as the scene is being released by the system. 54 | // This occurs shortly after the scene enters the background, or when its session is discarded. 55 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 56 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 57 | } 58 | 59 | func sceneDidBecomeActive(_ scene: UIScene) { 60 | // Called when the scene has moved from an inactive state to an active state. 61 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 62 | } 63 | 64 | func sceneWillResignActive(_ scene: UIScene) { 65 | // Called when the scene will move from an active state to an inactive state. 66 | // This may occur due to temporary interruptions (ex. an incoming phone call). 67 | } 68 | 69 | func sceneWillEnterForeground(_ scene: UIScene) { 70 | // Called as the scene transitions from the background to the foreground. 71 | // Use this method to undo the changes made on entering the background. 72 | } 73 | 74 | func sceneDidEnterBackground(_ scene: UIScene) { 75 | // Called as the scene transitions from the foreground to the background. 76 | // Use this method to save data, release shared resources, and store enough scene-specific state information 77 | // to restore the scene back to its current state. 78 | } 79 | 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Examples/SelectableBarChartExample-iOS/SelectableBarChartExample-iOS/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // SelectableBarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import UIKit 28 | import SwiftUI 29 | 30 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 31 | 32 | var window: UIWindow? 33 | 34 | 35 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 36 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 37 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 38 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 39 | 40 | // Create the SwiftUI view that provides the window contents. 41 | let contentView = ContentView() 42 | 43 | // Use a UIHostingController as window root view controller. 44 | if let windowScene = scene as? UIWindowScene { 45 | let window = UIWindow(windowScene: windowScene) 46 | window.rootViewController = UIHostingController(rootView: contentView) 47 | self.window = window 48 | window.makeKeyAndVisible() 49 | } 50 | } 51 | 52 | func sceneDidDisconnect(_ scene: UIScene) { 53 | // Called as the scene is being released by the system. 54 | // This occurs shortly after the scene enters the background, or when its session is discarded. 55 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 56 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 57 | } 58 | 59 | func sceneDidBecomeActive(_ scene: UIScene) { 60 | // Called when the scene has moved from an inactive state to an active state. 61 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 62 | } 63 | 64 | func sceneWillResignActive(_ scene: UIScene) { 65 | // Called when the scene will move from an active state to an inactive state. 66 | // This may occur due to temporary interruptions (ex. an incoming phone call). 67 | } 68 | 69 | func sceneWillEnterForeground(_ scene: UIScene) { 70 | // Called as the scene transitions from the background to the foreground. 71 | // Use this method to undo the changes made on entering the background. 72 | } 73 | 74 | func sceneDidEnterBackground(_ scene: UIScene) { 75 | // Called as the scene transitions from the foreground to the background. 76 | // Use this method to save data, release shared resources, and store enough scene-specific state information 77 | // to restore the scene back to its current state. 78 | } 79 | 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Examples/BarChartExample-watchOS/BarChartExample-watchOS WatchKit Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDelegate.swift 3 | // BarChartExample-watchOS WatchKit Extension 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import WatchKit 28 | 29 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 30 | 31 | func applicationDidFinishLaunching() { 32 | // Perform any final initialization of your application. 33 | } 34 | 35 | func applicationDidBecomeActive() { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillResignActive() { 40 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 41 | // Use this method to pause ongoing tasks, disable timers, etc. 42 | } 43 | 44 | func handle(_ backgroundTasks: Set) { 45 | // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one. 46 | for task in backgroundTasks { 47 | // Use a switch statement to check the task type 48 | switch task { 49 | case let backgroundTask as WKApplicationRefreshBackgroundTask: 50 | // Be sure to complete the background task once you’re done. 51 | backgroundTask.setTaskCompletedWithSnapshot(false) 52 | case let snapshotTask as WKSnapshotRefreshBackgroundTask: 53 | // Snapshot tasks have a unique completion call, make sure to set your expiration date 54 | snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil) 55 | case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask: 56 | // Be sure to complete the connectivity task once you’re done. 57 | connectivityTask.setTaskCompletedWithSnapshot(false) 58 | case let urlSessionTask as WKURLSessionRefreshBackgroundTask: 59 | // Be sure to complete the URL session task once you’re done. 60 | urlSessionTask.setTaskCompletedWithSnapshot(false) 61 | case let relevantShortcutTask as WKRelevantShortcutRefreshBackgroundTask: 62 | // Be sure to complete the relevant-shortcut task once you're done. 63 | relevantShortcutTask.setTaskCompletedWithSnapshot(false) 64 | case let intentDidRunTask as WKIntentDidRunRefreshBackgroundTask: 65 | // Be sure to complete the intent-did-run task once you're done. 66 | intentDidRunTask.setTaskCompletedWithSnapshot(false) 67 | default: 68 | // make sure to complete unhandled task types 69 | task.setTaskCompletedWithSnapshot(false) 70 | } 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Sources/BarChart/YAxisScaler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YAxisScaler.swift 3 | // BarChart 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | struct YAxisScaler { 30 | private var minPoint: Double 31 | private var maxPoint: Double 32 | private var maxTicks: Int 33 | private(set) var tickSpacing: Double? 34 | private(set) var scaledMin: Double? 35 | private(set) var scaledMax: Double? 36 | 37 | init(min: Double, max: Double, maxTicks: Int) { 38 | self.maxTicks = maxTicks 39 | self.minPoint = min 40 | self.maxPoint = max 41 | self.calculate() 42 | } 43 | 44 | func scaledValues() -> [Double] { 45 | guard let tickSpacing = self.tickSpacing, 46 | let min = self.scaledMin, 47 | let max = self.scaledMax else { return [] } 48 | var labels = [Double]() 49 | 50 | // Adjusted max to include actual max to the list 51 | let adjustedMax = max + tickSpacing / 2 52 | for label in stride(from: min, to: adjustedMax, by: tickSpacing) { 53 | labels.append(self.removeTrailingZeros(label, to: tickSpacing.decimalsCount())) 54 | } 55 | return labels 56 | } 57 | 58 | private mutating func calculate() { 59 | guard self.maxTicks > 1, 60 | self.maxPoint > self.minPoint else { return } 61 | let range = self.scale(self.maxPoint - self.minPoint, round: false) 62 | let tickSpacing = self.scale(range / Double((self.maxTicks - 1)), round: true) 63 | let scaledMin = floor(self.minPoint / tickSpacing) * tickSpacing 64 | self.scaledMin = self.removeTrailingZeros(scaledMin, to: tickSpacing.decimalsCount()) 65 | let scaledMax = ceil(self.maxPoint / tickSpacing) * tickSpacing 66 | self.scaledMax = self.removeTrailingZeros(scaledMax, to: tickSpacing.decimalsCount()) 67 | self.tickSpacing = tickSpacing 68 | } 69 | 70 | private func removeTrailingZeros(_ value: Double, to decimalsCount: Int) -> Double { 71 | return Double(String(format: "%.\(decimalsCount)f", value))! 72 | } 73 | 74 | private func scale(_ range: Double, round: Bool) -> Double { 75 | let exponent = floor(log10(range)) 76 | let fraction = range / pow(10, exponent) 77 | let niceFraction: Double 78 | 79 | if round { 80 | if fraction <= 1.5 { 81 | niceFraction = 1 82 | } else if fraction <= 3 { 83 | niceFraction = 2 84 | } else if fraction <= 7 { 85 | niceFraction = 5 86 | } else { 87 | niceFraction = 10 88 | } 89 | } else { 90 | if fraction <= 1 { 91 | niceFraction = 1 92 | } else if fraction <= 2 { 93 | niceFraction = 2 94 | } else if fraction <= 5 { 95 | niceFraction = 5 96 | } else { 97 | niceFraction = 10 98 | } 99 | } 100 | 101 | return niceFraction * pow(10, exponent) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // BarChartExample-tvOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | import BarChart 29 | 30 | struct ContentView: View { 31 | 32 | // MARK: - Chart Properties 33 | 34 | let chartHeight: CGFloat = 400 35 | let config = ChartConfiguration() 36 | @State var entries: [ChartDataEntry] = [] 37 | 38 | // MARK: - Controls Properties 39 | 40 | @State var maxEntriesCount: String = "" 41 | @State var xAxisTicksIntervalValue: String = "" 42 | @State var isXAxisTicksHidden: Bool = false 43 | 44 | // MARK: - Views 45 | 46 | var body: some View { 47 | NavigationView { 48 | ScrollView { 49 | VStack(spacing: 10) { 50 | self.chartView() 51 | self.controlsView() 52 | } 53 | .padding(5) 54 | } 55 | } 56 | } 57 | 58 | func chartView() -> some View { 59 | ZStack { 60 | // Drop shadow rectangle 61 | RoundedRectangle(cornerRadius: 5) 62 | .foregroundColor(.white) 63 | .padding(5) 64 | .shadow(color: .black, radius: 5) 65 | Text("No data").opacity(self.entries.isEmpty ? 1.0 : 0.0) 66 | BarChartView(config: self.config) 67 | .onAppear() { 68 | let labelsFont = CTFontCreateWithName(("SFProText-Regular" as CFString), 10, nil) 69 | self.config.data.color = .red 70 | self.config.xAxis.labelsColor = .gray 71 | self.config.xAxis.ticksColor = .gray 72 | self.config.labelsCTFont = labelsFont 73 | self.config.xAxis.ticksStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [2, 4]) 74 | self.config.yAxis.labelsColor = .gray 75 | self.config.yAxis.ticksColor = .gray 76 | self.config.yAxis.ticksStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [2, 4]) 77 | self.config.yAxis.minTicksSpacing = 30.0 78 | self.config.yAxis.formatter = { (value, decimals) in 79 | let format = value == 0 ? "" : "b" 80 | return String(format: "%.\(decimals)f\(format)", value) 81 | } 82 | } 83 | .animation(.easeInOut) 84 | .onReceive([self.isXAxisTicksHidden].publisher.first()) { (value) in 85 | self.config.xAxis.ticksColor = value ? .clear : .gray 86 | } 87 | .onReceive([self.xAxisTicksIntervalValue].publisher.first()) { (value) in 88 | self.config.xAxis.ticksInterval = Int(value) 89 | } 90 | .padding(15) 91 | }.frame(height: self.chartHeight) 92 | } 93 | 94 | func controlsView() -> some View { 95 | Group { 96 | TextField("Max entries count", text: self.$maxEntriesCount).padding(15) 97 | HStack { 98 | Button(action: { 99 | let newEntries = self.randomEntries() 100 | self.entries = newEntries 101 | self.config.data.entries = newEntries 102 | }) { 103 | Text("Generate entries") 104 | } 105 | Button(action: { 106 | self.config.data.color = Color.random 107 | }) { 108 | Text("Generate color") 109 | } 110 | Button(action: { 111 | self.config.data.gradientColor = GradientColor(start: Color.random, end: Color.random) 112 | }) { 113 | Text("Generate gradient") 114 | } 115 | } 116 | TextField("x- axis ticks interval", text: self.$xAxisTicksIntervalValue).padding(15) 117 | Toggle(isOn: self.$isXAxisTicksHidden, label: { 118 | Text("X axis ticks is hidden") 119 | }).padding(15) 120 | } 121 | } 122 | 123 | // MARK: - Random Helpers 124 | 125 | func randomEntries() -> [ChartDataEntry] { 126 | var entries = [ChartDataEntry]() 127 | guard let maxEntriesCount = Int(self.maxEntriesCount), maxEntriesCount > 0 else { return [] } 128 | for data in 0.. some View { 79 | VStack(alignment: .leading, spacing: 0) { 80 | self.selectionIndicatorView() 81 | self.chartView() 82 | } 83 | .frame(height: chartHeight) 84 | .padding(15) 85 | } 86 | 87 | func miniSelectableChartView() -> some View { 88 | SelectableBarChartView(config: self.config) 89 | .onBarSelection { entry, location in 90 | self.selectedBarTopCentreLocation = location 91 | self.selectedEntry = entry 92 | } 93 | .selectionView { 94 | MiniSelectionIndicator(entry: self.selectedEntry, 95 | location: self.selectedBarTopCentreLocation) 96 | } 97 | .frame(height: self.chartHeight - self.selectionIndicatorHeight) 98 | .padding(15) 99 | } 100 | 101 | func chartView() -> some View { 102 | GeometryReader { proxy in 103 | SelectableBarChartView(config: self.config) 104 | .onBarSelection { entry, location in 105 | self.selectedBarTopCentreLocation = location 106 | self.selectedEntry = entry 107 | } 108 | .selectionView { 109 | SelectionLine(location: self.selectedBarTopCentreLocation, 110 | height: proxy.size.height - 17) 111 | } 112 | } 113 | } 114 | 115 | func selectionIndicatorView() -> some View { 116 | Group { 117 | if self.selectedEntry != nil && self.selectedBarTopCentreLocation != nil { 118 | SelectionIndicator(entry: self.selectedEntry!, 119 | location: self.selectedBarTopCentreLocation!.x, 120 | infoRectangleColor: Color(red: 241/255, green: 242/255, blue: 245/255)) 121 | } else { 122 | Rectangle().foregroundColor(.clear) 123 | } 124 | } 125 | .frame(height: self.selectionIndicatorHeight) 126 | } 127 | 128 | func randomEntries() -> [ChartDataEntry] { 129 | var entries = [ChartDataEntry]() 130 | for data in 0..<15 { 131 | let randomDouble = Double.random(in: -20...50) 132 | let newEntry = ChartDataEntry(x: "\(2000+data)", y: randomDouble) 133 | entries.append(newEntry) 134 | } 135 | return entries 136 | } 137 | 138 | func resetSelection() { 139 | self.selectedBarTopCentreLocation = nil 140 | self.selectedEntry = nil 141 | } 142 | } 143 | 144 | struct ContentView_Previews: PreviewProvider { 145 | static var previews: some View { 146 | ContentView() 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // BarChartExample-macOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | import BarChart 29 | 30 | struct ContentView: View { 31 | 32 | // MARK: - Chart Properties 33 | 34 | let chartHeight: CGFloat = 300 35 | let config = ChartConfiguration() 36 | @State var entries: [ChartDataEntry] = [] 37 | 38 | // MARK: - Controls Properties 39 | 40 | @State var maxEntriesCount: Int = 0 41 | @State var xAxisTicksIntervalValue: Double = 1 42 | @State var isXAxisTicksHidden: Bool = false 43 | 44 | // MARK: - Views 45 | 46 | var body: some View { 47 | NavigationView { 48 | ScrollView { 49 | VStack(spacing: 10) { 50 | self.chartView() 51 | self.controlsView() 52 | } 53 | .padding(5) 54 | } 55 | } 56 | } 57 | 58 | func chartView() -> some View { 59 | ZStack { 60 | // Drop shadow rectangle 61 | RoundedRectangle(cornerRadius: 5) 62 | .foregroundColor(.white) 63 | .padding(5) 64 | .shadow(color: .black, radius: 5) 65 | Text("No data").opacity(self.entries.isEmpty ? 1.0 : 0.0) 66 | BarChartView(config: self.config) 67 | .onAppear() { 68 | let labelsFont = CTFontCreateWithName(("SFProText-Regular" as CFString), 10, nil) 69 | self.config.data.color = .red 70 | self.config.xAxis.labelsColor = .gray 71 | self.config.xAxis.ticksColor = .gray 72 | self.config.labelsCTFont = labelsFont 73 | self.config.xAxis.ticksStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [2, 4]) 74 | self.config.yAxis.labelsColor = .gray 75 | self.config.yAxis.ticksColor = .gray 76 | self.config.yAxis.ticksStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [2, 4]) 77 | self.config.yAxis.minTicksSpacing = 30.0 78 | self.config.yAxis.formatter = { (value, decimals) in 79 | let format = value == 0 ? "" : "b" 80 | return String(format: "%.\(decimals)f\(format)", value) 81 | } 82 | } 83 | .animation(.easeInOut) 84 | .onReceive([self.isXAxisTicksHidden].publisher.first()) { (value) in 85 | self.config.xAxis.ticksColor = value ? .clear : .gray 86 | } 87 | .onReceive([self.xAxisTicksIntervalValue].publisher.first()) { (value) in 88 | self.config.xAxis.ticksInterval = Int(value) 89 | } 90 | .padding(15) 91 | }.frame(height: self.chartHeight) 92 | } 93 | 94 | func controlsView() -> some View { 95 | Group { 96 | Stepper(value: self.$maxEntriesCount, in: 0...30) { 97 | Text("Max entries count: \(self.maxEntriesCount)") 98 | }.padding(15) 99 | HStack { 100 | Button(action: { 101 | let newEntries = self.randomEntries() 102 | self.entries = newEntries 103 | self.config.data.entries = newEntries 104 | }) { 105 | Text("Generate entries") 106 | } 107 | Button(action: { 108 | self.config.data.color = Color.random 109 | }) { 110 | Text("Generate color") 111 | } 112 | Button(action: { 113 | self.config.data.gradientColor = GradientColor(start: Color.random, end: Color.random) 114 | }) { 115 | Text("Generate gradient") 116 | } 117 | } 118 | HStack { 119 | Stepper(value: self.$xAxisTicksIntervalValue, in: 1...4) { 120 | Text("X axis ticks interval: \(Int(self.xAxisTicksIntervalValue))") 121 | }.padding(15) 122 | Toggle(isOn: self.$isXAxisTicksHidden, label: { 123 | Text("X axis ticks is hidden") 124 | }).padding(15) 125 | } 126 | } 127 | } 128 | 129 | // MARK: - Random Helpers 130 | 131 | func randomEntries() -> [ChartDataEntry] { 132 | var entries = [ChartDataEntry]() 133 | guard self.maxEntriesCount > 0 else { return [] } 134 | for data in 0.. Path { 61 | var vLine = Path() 62 | vLine.move(to: self.points.0) 63 | vLine.addLine(to: self.points.1) 64 | return vLine 65 | } 66 | } 67 | 68 | struct LabelView: View { 69 | let text: String 70 | let ctFont: CTFont 71 | let color: Color 72 | 73 | var body: some View { 74 | Text(self.text) 75 | .font(Font(self.ctFont)) 76 | .foregroundColor(self.color) 77 | } 78 | } 79 | 80 | struct XAxisView: View { 81 | let xAxis: XAxis 82 | let frameSize: CGSize 83 | 84 | var body: some View { 85 | ForEach((0.. CGFloat { 100 | let tickX = self.tickX(at: index) 101 | let x = tickX - self.frameSize.width / 2 102 | return x 103 | } 104 | 105 | func tickX(at index: Int) -> CGFloat { 106 | let chartEntry = self.xAxis.chartEntry(at: index) 107 | guard let indexAtFullRange = self.xAxis.data.firstIndex(where: { $0 == chartEntry }), 108 | let centre = self.xAxis.layout.barCentre(at: indexAtFullRange) else { return 0 } 109 | return centre 110 | } 111 | 112 | func tickPoints(index: Int) -> (CGPoint, CGPoint) { 113 | let x = self.tickX(at: index) 114 | let labelsHeight = String().height(ctFont: self.xAxis.labelsCTFont) 115 | let startY = labelsHeight / 2 116 | let endY = self.frameSize.height - labelsHeight * 1.5 117 | return (CGPoint(x: x, y: startY), CGPoint(x: x, y: endY)) 118 | } 119 | } 120 | 121 | struct YAxisView: View { 122 | let yAxis: YAxis 123 | let frameSize: CGSize 124 | 125 | var body: some View { 126 | ForEach((0.. StrokeStyle { 141 | var style = self.yAxis.ref.ticksStyle 142 | if self.yAxis.labelValue(at: index) == 0 { 143 | style.dash = [] 144 | return style 145 | } 146 | return style 147 | } 148 | 149 | func labelOffsetY(at index: Int) -> CGFloat { 150 | let tickY = self.tickY(at: index) 151 | let height = self.frameSize.height 152 | let y = (height - tickY) - (height / 2) 153 | return y 154 | } 155 | 156 | func tickY(at index: Int) -> CGFloat { 157 | guard let chartMin = self.yAxis.scaler?.scaledMin, 158 | let pixelsRatio = self.yAxis.pixelsRatio(), 159 | let label = self.yAxis.labelValue(at: index) else { return 0 } 160 | let shift = String().height(ctFont: self.yAxis.labelsCTFont) * 1.5 161 | return (CGFloat(label) - CGFloat(chartMin)) * pixelsRatio + shift 162 | } 163 | 164 | func tickPoints(index: Int) -> (CGPoint, CGPoint) { 165 | let y = self.tickY(at: index) 166 | return self.tickPoints(y: y) 167 | } 168 | 169 | func tickPoints(y: CGFloat) -> (CGPoint, CGPoint) { 170 | let endPointX = self.frameSize.width - self.yAxis.maxLabelWidth 171 | return (CGPoint(x: 0, y: y), CGPoint(x: endPointX, y: y)) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /Examples/BarChartExample-iOS/BarChartExample-iOS/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // BarChartExample-iOS 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import SwiftUI 28 | import BarChart 29 | 30 | struct ContentView: View { 31 | 32 | let orientationChanged = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification) 33 | .makeConnectable() 34 | .autoconnect() 35 | 36 | // MARK: - Chart Properties 37 | 38 | let chartHeight: CGFloat = 400 39 | let config = ChartConfiguration() 40 | @State var entries = [ChartDataEntry]() 41 | @State var selectedBarTopCentreLocation: CGPoint? 42 | @State var selectedEntry: ChartDataEntry? 43 | 44 | // MARK: - Controls Properties 45 | 46 | @State var maxEntriesCount: Int = 0 47 | @State var xAxisTicksIntervalValue: Double = 1 48 | @State var isXAxisTicksHidden: Bool = false 49 | 50 | // MARK: - Views 51 | 52 | var body: some View { 53 | NavigationView { 54 | ScrollView { 55 | VStack(spacing: 10) { 56 | self.chartView() 57 | self.controlsView() 58 | .navigationBarTitle(Text("BarChart")) 59 | } 60 | .padding(5) 61 | } 62 | }.navigationViewStyle(StackNavigationViewStyle()) 63 | } 64 | 65 | func selectionIndicatorView() -> some View { 66 | Group { 67 | if self.selectedEntry != nil && self.selectedBarTopCentreLocation != nil { 68 | SelectionIndicator(entry: self.selectedEntry!, 69 | location: self.selectedBarTopCentreLocation!.x, 70 | infoRectangleColor: Color(red: 241/255, green: 242/255, blue: 245/255)) 71 | } else { 72 | Rectangle().foregroundColor(.clear) 73 | } 74 | } 75 | .frame(height: 60) 76 | } 77 | 78 | func chartView() -> some View { 79 | ZStack { 80 | // Drop shadow rectangle 81 | RoundedRectangle(cornerRadius: 5) 82 | .foregroundColor(.white) 83 | .padding(5) 84 | .shadow(color: .black, radius: 5) 85 | Text("No data").opacity(self.entries.isEmpty ? 1.0 : 0.0) 86 | VStack(alignment: .leading, spacing: 0) { 87 | self.selectionIndicatorView() 88 | SelectableBarChartView(config: self.config) 89 | .onBarSelection { entry, location in 90 | self.selectedBarTopCentreLocation = location 91 | self.selectedEntry = entry 92 | } 93 | .selectionView { 94 | SelectionLine(location: self.selectedBarTopCentreLocation, 95 | height: 295) 96 | } 97 | .onAppear() { 98 | let labelsFont = CTFontCreateWithName(("SFProText-Regular" as CFString), 10, nil) 99 | self.config.data.entries = self.randomEntries() 100 | self.config.data.color = .red 101 | self.config.xAxis.labelsColor = .gray 102 | self.config.xAxis.ticksColor = .gray 103 | self.config.labelsCTFont = labelsFont 104 | self.config.xAxis.ticksStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [2, 4]) 105 | self.config.yAxis.labelsColor = .gray 106 | self.config.yAxis.ticksColor = .gray 107 | self.config.yAxis.ticksStyle = StrokeStyle(lineWidth: 1.5, lineCap: .round, dash: [2, 4]) 108 | self.config.yAxis.minTicksSpacing = 30.0 109 | self.config.yAxis.formatter = { (value, decimals) in 110 | let format = value == 0 ? "" : "b" 111 | return String(format: " %.\(decimals)f\(format)", value) 112 | } 113 | } 114 | .animation(.easeInOut) 115 | .onReceive([self.isXAxisTicksHidden].publisher.first()) { (value) in 116 | self.config.xAxis.ticksColor = value ? .clear : .gray 117 | } 118 | .onReceive([self.xAxisTicksIntervalValue].publisher.first()) { (value) in 119 | self.config.xAxis.ticksInterval = Int(value) 120 | } 121 | .onReceive(self.orientationChanged) { _ in 122 | self.config.objectWillChange.send() 123 | } 124 | }.padding(15) 125 | }.frame(height: self.chartHeight) 126 | } 127 | 128 | func controlsView() -> some View { 129 | Group { 130 | VStack(spacing: 0) { 131 | Stepper(value: self.$maxEntriesCount, in: 0...30) { 132 | Text("Max entries count: \(self.maxEntriesCount)") 133 | }.padding(15) 134 | Button(action: { 135 | let newEntries = self.randomEntries() 136 | self.entries = newEntries 137 | self.config.data.entries = newEntries 138 | }) { 139 | Text("Generate entries") 140 | }.randomButtonStyle() 141 | } 142 | HStack { 143 | Button(action: { 144 | self.config.data.color = Color.random 145 | }) { 146 | Text("Generate color") 147 | }.randomButtonStyle() 148 | Button(action: { 149 | self.config.data.gradientColor = GradientColor(start: Color.random, end: Color.random) 150 | }) { 151 | Text("Generate gradient") 152 | }.randomButtonStyle() 153 | } 154 | Stepper(value: self.$xAxisTicksIntervalValue, in: 1...4) { 155 | Text("X axis ticks interval: \(Int(self.xAxisTicksIntervalValue))") 156 | }.padding(15) 157 | Toggle(isOn: self.$isXAxisTicksHidden, label: { 158 | Text("X axis ticks is hidden") 159 | }).padding(15) 160 | } 161 | } 162 | 163 | // MARK: - Random Helpers 164 | 165 | func randomEntries() -> [ChartDataEntry] { 166 | var entries = [ChartDataEntry]() 167 | guard self.maxEntriesCount > 0 else { return [] } 168 | for data in 0.. some View { 189 | content 190 | .padding(10) 191 | .background(Color.gray.opacity(0.2)) 192 | .cornerRadius(8) 193 | } 194 | } 195 | 196 | extension View { 197 | func randomButtonStyle() -> some View { 198 | self.modifier(RandomButtonStyle()) 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Tests/BarChartTests/TestData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestData.swift 3 | // BarChartTests 4 | // 5 | // Copyright (c) 2020 Roman Baitaliuk 6 | // Permission is hereby granted, free of charge, to any person 7 | // obtaining a copy of this software and associated documentation 8 | // files (the "Software"), to deal in the Software without 9 | // restriction, including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the 12 | // Software is furnished to do so, subject to the following 13 | // conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | // OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | import Foundation 28 | @testable import BarChart 29 | 30 | struct TestData { 31 | 32 | static func generate(with values: [Double]) -> [ChartDataEntry] { 33 | var entries = [ChartDataEntry]() 34 | for (index, value) in values.enumerated() { 35 | let newEntry = ChartDataEntry(x: "\(index)", y: value) 36 | entries.append(newEntry) 37 | } 38 | return entries 39 | } 40 | 41 | struct Values { 42 | struct Positive { 43 | struct Small { 44 | static let values1: [Double] = [0.23875672295222253] 45 | static let values2: [Double] = [0.2684136185901309, 0.16131669522994863] 46 | static let values3: [Double] = [0.2639830126014629, 0.15974664769619834, 0.22752326268788364, 0.13911307358474653, 0.28026824376151305] 47 | static let values4: [Double] = [0.19727250308928962, 0.2308106471922598, 0.13256217023006187, 0.1367648372664736, 0.15714402753315498, 0.22588350803628962, 0.2644809090012066] 48 | static let values5: [Double] = [0.5466323238920077, 0.5156455479675484, 0.13291550274624686, 0.7771603858442256, 0.9025048107614076, 0.40737263782697297, 0.6655120656097606, 0.647796170763738, 0.3207767946207413, 0.2868540478265839, 0.2620104724701646, 0.8367088377208742, 0.21919262237132098, 0.1722920524489231, 0.15922626432748044, 0.4891199176880685, 0.23470257555145846, 0.8706489523436453, 0.8311258151046474, 0.5778233129883721, 0.8526875093676144] 49 | } 50 | 51 | struct Mid { 52 | static let values1: [Double] = [98.40067879858509] 53 | static let values2: [Double] = [55.052756589112676, 69.48356595339442] 54 | static let values3: [Double] = [40.57943586235161, 54.6206979706237, 43.43520250456608, 31.636360365610194, 42.312501067159566, 59.874004415953074] 55 | static let values4: [Double] = [46.23376707364597, 78.1047124748174, 96.76482212585093, 10.875552416027393, 4.145508110947263, 19.89589074913821, 57.87656976286651, 51.427645821760045] 56 | static let values5: [Double] = [49.172347022669214, 62.638089607406826, 39.332530499289774, 98.51617133533895, 89.76300260279845, 53.48150921316537, 50.61944810303782, 86.73384074151308, 6.481826092252144, 39.76813483486283, 42.43219019704493, 95.85471451668741, 6.639584266033531, 82.20578895864162, 57.20033695917166, 19.549376342682226, 9.526520334855517, 15.954728800028686] 57 | } 58 | 59 | struct Large { 60 | static let values1: [Double] = [933.608090793989] 61 | static let values2: [Double] = [260.29939965518247, 461.8232573168553] 62 | static let values3: [Double] = [212.20237931356752, 134.26672344492164, 170.78818158912125, 726.9360462508057, 433.4176900479346, 853.6549857725398] 63 | static let values4: [Double] = [172.6209415482361, 619.3983725348953, 613.0122947735748, 411.4339313375111, 959.4363998296446, 613.204165830946, 499.49495683521883, 591.6883034445457, 288.30229436515344, 722.2358023940463] 64 | static let values5: [Double] = [485.1778745682676, 372.1598793295034, 937.5620722111946, 667.034524273335, 645.3795123800068, 151.02751655585465, 801.0126816467382, 709.3331910204965, 950.5785950961556, 614.2357925588653, 848.0686234611463, 172.26868259108699, 990.3253385258535, 177.4444336874585, 677.0505570069992, 860.3715740108657, 584.0133371945478, 830.5029774825001, 103.07842008613726, 411.27737210941496, 803.2825484502141] 65 | } 66 | } 67 | 68 | struct Negative { 69 | struct Small { 70 | static let values1: [Double] = [-0.2362110700260261] 71 | static let values2: [Double] = [-0.11719829568796988, -0.1995677967246154] 72 | static let values3: [Double] = [-0.2882649374950868, -0.1328421271991442, -0.26172464939834195, -0.25592332283759134, -0.2887961694356732, -0.25240426028516927] 73 | static let values4: [Double] = [-0.2523331512221314, -0.18935587960194128, -0.1507128289270137, -0.2473339367312253, -0.11248666006869831, -0.1135295222658772, -0.14617562966908498] 74 | static let values5: [Double] = [-0.14539804216132646, -0.29087341537264905, -0.2791428956717379, -0.12177629522748437, -0.21288959751910208, -0.26150650039906886, -0.16288768820222366, -0.26280536120587306, -0.11326897716694947, -0.17391125484591427, -0.10603393446904713, -0.17493115249989497, -0.1022184734124871, -0.1483562776481696, -0.16983033913139306, -0.18051178923391525, -0.28008667534171316, -0.11474472761680429, -0.12902809125618483, -0.16910750985712852, -0.200144420741012] 75 | } 76 | 77 | struct Mid { 78 | static let values1: [Double] = [-14.317389855452234] 79 | static let values2: [Double] = [-40.44939166476918, -5.267268367336996] 80 | static let values3: [Double] = [-25.948740362913725, -25.864675726579208, -20.201968323409645, -4.5575961274022205, -43.374370609825185, -48.50547096385824, -73.3148551940273] 81 | static let values4: [Double] = [-23.32930085055102, -44.81745644382531, -58.26116506455848, -21.868106648509922, -77.66153242628351, -61.74060343537626, -95.28112276855798, -20.72286052859303, -36.07821463080642, -9.036730358526029, -21.34876740902675] 82 | static let values5: [Double] = [-29.377128218151938, -43.583959440724, -60.57144998506984, -27.912392970038724, -67.66153493841162, -70.30545485895426, -46.609860516676626, -80.6715155854962, -21.121938873732148, -3.267065074012308, -52.523454355897, -15.809916074446946, -88.4664530162171, -53.46209175820739, -98.24645012760485, -40.621009321511025, -75.7607035928469, -38.65992086484787, -25.06331734152529, -78.77873313645318, -2.9205965090560397] 83 | } 84 | 85 | struct Large { 86 | static let values1: [Double] = [-239.74265265469944] 87 | static let values2: [Double] = [-193.61155473573785, -368.1931642646799] 88 | static let values3: [Double] = [-263.8260844385155, -379.47607946315, -182.0835402863488, -929.4225571744635, -135.37498938480132, -209.39533249920567] 89 | static let values4: [Double] = [-482.9912080921938, -543.0718270711238, -841.6834886284742, -734.3505573873854, -473.230466741987, -125.96731083377028, -950.3737016579273, -570.0150207483283, -402.2457519917757, -852.9327226621378, -866.820088496364, -749.8733270188756, -725.0817569634371, -964.5704647521372, -350.6768794744197, -693.8371332429392, -887.906685288875] 90 | static let values5: [Double] = [-682.298899071088, -289.8263516807647, -818.3603392841937, -156.83179737381136, -473.12448740337095, -204.6093884182285, -146.3741735870775, -771.3164241007768, -309.95411564741835, -532.0014464500366, -127.9993431009284, -223.90190879367856, -121.77408461442997, -479.21421264087246, -553.9472924202291, -980.8133668245063, -328.23929968378593, -367.45499321001125, -960.5696614584499, -697.1179671816379, -333.80047277900246] 91 | } 92 | } 93 | 94 | struct PositiveAndNegative { 95 | struct Small { 96 | static let values1: [Double] = [-0.1335971968625974, 0.15042626326352287] 97 | static let values2: [Double] = [-0.19179890231919727, -0.19824635093222207, -0.05614434058402015, 0.16531352492670082, -0.10222339146220016, -0.17616552491728152] 98 | static let values3: [Double] = [0.057043207052957345, -0.17266114786523465, -0.09896188581597838, 0.1634180468399561, 0.1535860219929393, -0.06401882651635035, -0.08171773896238582, -0.12653024551489617, -0.12004760971991035, 0.10813657584652997, 0.14047068164695253, 0.17068410990347432] 99 | static let values4: [Double] = [0.12667162089982958, 0.17602664191040757, -0.10828495319777849, 0.07979324073295657, 0.13153427513942845, -0.10111136704122692, 0.11681627329019051, -0.05903421856412311, 0.19521921218263705, -0.02370810151114977, 0.06461703898036253, 0.13998786150718262, -0.11201883397754818, -0.09976159758237757, -0.06217398412416958, -0.12906739295486927, -0.12133652920981391, -0.1271372430804091, 0.0274669493943423, 0.1583307792565014, -0.06211459861341548] 100 | } 101 | 102 | struct Mid { 103 | static let values1: [Double] = [23.339024477255577, -12.441113408473313] 104 | static let values2: [Double] = [-57.836785834777515, -16.987389371760187, -63.09884786907845, -3.3619202228148026, -83.07094357032865, 63.49941469148311, 36.21429109196649] 105 | static let values3: [Double] = [56.823797332560076, -9.357855660626186, 86.79928262657432, 56.28186010394933, 10.37623574317368, -42.93800045609437, 22.666713287829793, 28.987019069115746, -51.07790280394842, -41.19925956467978, 34.99749052093938] 106 | static let values4: [Double] = [-80.9558114658461, -67.43064256340811, -31.010891918466513, -16.32131696464714, -61.831198991633585, -82.08059846836338, -30.70622918552634, -92.0481265531018, -80.01410560184858, -82.78945014138348, -64.95836971316137, -37.965892989649944, 54.22067420486641, -26.048142477280933, -39.952902182187145, 83.91447831477453, 5.514409071277669, -10.492105497958562, 61.820823940755304, 62.98528862708713, -20.57484781633869] 107 | } 108 | 109 | struct Large { 110 | static let values1: [Double] = [-411.5522077713565, 452.5998797184061] 111 | static let values2: [Double] = [679.0210908916486, -64.84909457164622, 735.5191229658603, -977.9345158798944, 784.6456208118452, -547.5630323624714, -759.5371547032937, -895.6974715191906] 112 | static let values3: [Double] = [-554.6985982768471, 635.2717161738949, 988.9598813698935, -992.9083819967719, 160.33476011719517, -220.36808281929598, -514.4973029310049, -895.4112690956259, 815.015255945395, -5.453655979820837] 113 | static let values4: [Double] = [-352.5312506773248, 696.7781263271556, -826.1156693622315, -826.6704401550977, -986.5498962658393, -916.2192263655826, -219.01079748666393, -275.6620455326597, 75.32925703258411, -837.5219362769275, -72.8034295891922, 501.8440974667251, 215.25711662569665, 679.3870482501363, -614.2731047486398, -864.5668565796616, 931.4398647576736, 128.20720034681813, -735.4685658273128, 419.3802969561673, -925.4498519189034] 114 | } 115 | } 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Examples/BarChartExample-tvOS/BarChartExample-tvOS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D317956D24C1B27F00DC2168 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D317956C24C1B27F00DC2168 /* AppDelegate.swift */; }; 11 | D317956F24C1B27F00DC2168 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D317956E24C1B27F00DC2168 /* ContentView.swift */; }; 12 | D317957124C1B27F00DC2168 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D317957024C1B27F00DC2168 /* Assets.xcassets */; }; 13 | D317957424C1B27F00DC2168 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D317957324C1B27F00DC2168 /* Preview Assets.xcassets */; }; 14 | D317957724C1B27F00DC2168 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D317957524C1B27F00DC2168 /* LaunchScreen.storyboard */; }; 15 | D399CAF0265C7F1A00A92054 /* BarChart in Frameworks */ = {isa = PBXBuildFile; productRef = D399CAEF265C7F1A00A92054 /* BarChart */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | D317956924C1B27F00DC2168 /* BarChartExample-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "BarChartExample-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | D317956C24C1B27F00DC2168 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | D317956E24C1B27F00DC2168 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 22 | D317957024C1B27F00DC2168 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 23 | D317957324C1B27F00DC2168 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 24 | D317957624C1B27F00DC2168 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 25 | D317957824C1B27F00DC2168 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | D399CAED265C7F1000A92054 /* BarChart */ = {isa = PBXFileReference; lastKnownFileType = folder; name = BarChart; path = ../..; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | D317956624C1B27F00DC2168 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | D399CAF0265C7F1A00A92054 /* BarChart in Frameworks */, 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | D317956024C1B27F00DC2168 = { 42 | isa = PBXGroup; 43 | children = ( 44 | D399CAED265C7F1000A92054 /* BarChart */, 45 | D317956B24C1B27F00DC2168 /* BarChartExample-tvOS */, 46 | D317956A24C1B27F00DC2168 /* Products */, 47 | D399CAEE265C7F1A00A92054 /* Frameworks */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | D317956A24C1B27F00DC2168 /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | D317956924C1B27F00DC2168 /* BarChartExample-tvOS.app */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | D317956B24C1B27F00DC2168 /* BarChartExample-tvOS */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | D317956C24C1B27F00DC2168 /* AppDelegate.swift */, 63 | D317956E24C1B27F00DC2168 /* ContentView.swift */, 64 | D317957024C1B27F00DC2168 /* Assets.xcassets */, 65 | D317957524C1B27F00DC2168 /* LaunchScreen.storyboard */, 66 | D317957824C1B27F00DC2168 /* Info.plist */, 67 | D317957224C1B27F00DC2168 /* Preview Content */, 68 | ); 69 | path = "BarChartExample-tvOS"; 70 | sourceTree = ""; 71 | }; 72 | D317957224C1B27F00DC2168 /* Preview Content */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | D317957324C1B27F00DC2168 /* Preview Assets.xcassets */, 76 | ); 77 | path = "Preview Content"; 78 | sourceTree = ""; 79 | }; 80 | D399CAEE265C7F1A00A92054 /* Frameworks */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | ); 84 | name = Frameworks; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | D317956824C1B27F00DC2168 /* BarChartExample-tvOS */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = D317957B24C1B27F00DC2168 /* Build configuration list for PBXNativeTarget "BarChartExample-tvOS" */; 93 | buildPhases = ( 94 | D317956524C1B27F00DC2168 /* Sources */, 95 | D317956624C1B27F00DC2168 /* Frameworks */, 96 | D317956724C1B27F00DC2168 /* Resources */, 97 | ); 98 | buildRules = ( 99 | ); 100 | dependencies = ( 101 | ); 102 | name = "BarChartExample-tvOS"; 103 | packageProductDependencies = ( 104 | D399CAEF265C7F1A00A92054 /* BarChart */, 105 | ); 106 | productName = "BarChartExample-tvOS"; 107 | productReference = D317956924C1B27F00DC2168 /* BarChartExample-tvOS.app */; 108 | productType = "com.apple.product-type.application"; 109 | }; 110 | /* End PBXNativeTarget section */ 111 | 112 | /* Begin PBXProject section */ 113 | D317956124C1B27F00DC2168 /* Project object */ = { 114 | isa = PBXProject; 115 | attributes = { 116 | LastSwiftUpdateCheck = 1160; 117 | LastUpgradeCheck = 1240; 118 | ORGANIZATIONNAME = "Roman Baitaliuk"; 119 | TargetAttributes = { 120 | D317956824C1B27F00DC2168 = { 121 | CreatedOnToolsVersion = 11.6; 122 | }; 123 | }; 124 | }; 125 | buildConfigurationList = D317956424C1B27F00DC2168 /* Build configuration list for PBXProject "BarChartExample-tvOS" */; 126 | compatibilityVersion = "Xcode 9.3"; 127 | developmentRegion = en; 128 | hasScannedForEncodings = 0; 129 | knownRegions = ( 130 | en, 131 | Base, 132 | ); 133 | mainGroup = D317956024C1B27F00DC2168; 134 | packageReferences = ( 135 | ); 136 | productRefGroup = D317956A24C1B27F00DC2168 /* Products */; 137 | projectDirPath = ""; 138 | projectRoot = ""; 139 | targets = ( 140 | D317956824C1B27F00DC2168 /* BarChartExample-tvOS */, 141 | ); 142 | }; 143 | /* End PBXProject section */ 144 | 145 | /* Begin PBXResourcesBuildPhase section */ 146 | D317956724C1B27F00DC2168 /* Resources */ = { 147 | isa = PBXResourcesBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | D317957724C1B27F00DC2168 /* LaunchScreen.storyboard in Resources */, 151 | D317957424C1B27F00DC2168 /* Preview Assets.xcassets in Resources */, 152 | D317957124C1B27F00DC2168 /* Assets.xcassets in Resources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXResourcesBuildPhase section */ 157 | 158 | /* Begin PBXSourcesBuildPhase section */ 159 | D317956524C1B27F00DC2168 /* Sources */ = { 160 | isa = PBXSourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | D317956F24C1B27F00DC2168 /* ContentView.swift in Sources */, 164 | D317956D24C1B27F00DC2168 /* AppDelegate.swift in Sources */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXSourcesBuildPhase section */ 169 | 170 | /* Begin PBXVariantGroup section */ 171 | D317957524C1B27F00DC2168 /* LaunchScreen.storyboard */ = { 172 | isa = PBXVariantGroup; 173 | children = ( 174 | D317957624C1B27F00DC2168 /* Base */, 175 | ); 176 | name = LaunchScreen.storyboard; 177 | sourceTree = ""; 178 | }; 179 | /* End PBXVariantGroup section */ 180 | 181 | /* Begin XCBuildConfiguration section */ 182 | D317957924C1B27F00DC2168 /* Debug */ = { 183 | isa = XCBuildConfiguration; 184 | buildSettings = { 185 | ALWAYS_SEARCH_USER_PATHS = NO; 186 | CLANG_ANALYZER_NONNULL = YES; 187 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 189 | CLANG_CXX_LIBRARY = "libc++"; 190 | CLANG_ENABLE_MODULES = YES; 191 | CLANG_ENABLE_OBJC_ARC = YES; 192 | CLANG_ENABLE_OBJC_WEAK = YES; 193 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_COMMA = YES; 196 | CLANG_WARN_CONSTANT_CONVERSION = YES; 197 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 198 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 199 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 200 | CLANG_WARN_EMPTY_BODY = YES; 201 | CLANG_WARN_ENUM_CONVERSION = YES; 202 | CLANG_WARN_INFINITE_RECURSION = YES; 203 | CLANG_WARN_INT_CONVERSION = YES; 204 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 205 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 206 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 208 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 209 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 210 | CLANG_WARN_STRICT_PROTOTYPES = YES; 211 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 212 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 213 | CLANG_WARN_UNREACHABLE_CODE = YES; 214 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 215 | COPY_PHASE_STRIP = NO; 216 | DEBUG_INFORMATION_FORMAT = dwarf; 217 | ENABLE_STRICT_OBJC_MSGSEND = YES; 218 | ENABLE_TESTABILITY = YES; 219 | GCC_C_LANGUAGE_STANDARD = gnu11; 220 | GCC_DYNAMIC_NO_PIC = NO; 221 | GCC_NO_COMMON_BLOCKS = YES; 222 | GCC_OPTIMIZATION_LEVEL = 0; 223 | GCC_PREPROCESSOR_DEFINITIONS = ( 224 | "DEBUG=1", 225 | "$(inherited)", 226 | ); 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 234 | MTL_FAST_MATH = YES; 235 | ONLY_ACTIVE_ARCH = YES; 236 | SDKROOT = appletvos; 237 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 238 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 239 | TVOS_DEPLOYMENT_TARGET = 13.4; 240 | }; 241 | name = Debug; 242 | }; 243 | D317957A24C1B27F00DC2168 /* Release */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_ENABLE_OBJC_WEAK = YES; 254 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 255 | CLANG_WARN_BOOL_CONVERSION = YES; 256 | CLANG_WARN_COMMA = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 261 | CLANG_WARN_EMPTY_BODY = YES; 262 | CLANG_WARN_ENUM_CONVERSION = YES; 263 | CLANG_WARN_INFINITE_RECURSION = YES; 264 | CLANG_WARN_INT_CONVERSION = YES; 265 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 266 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 270 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 271 | CLANG_WARN_STRICT_PROTOTYPES = YES; 272 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 273 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 274 | CLANG_WARN_UNREACHABLE_CODE = YES; 275 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_NS_ASSERTIONS = NO; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu11; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | MTL_ENABLE_DEBUG_INFO = NO; 289 | MTL_FAST_MATH = YES; 290 | SDKROOT = appletvos; 291 | SWIFT_COMPILATION_MODE = wholemodule; 292 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 293 | TVOS_DEPLOYMENT_TARGET = 13.4; 294 | VALIDATE_PRODUCT = YES; 295 | }; 296 | name = Release; 297 | }; 298 | D317957C24C1B27F00DC2168 /* Debug */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 302 | CODE_SIGN_STYLE = Manual; 303 | DEVELOPMENT_ASSET_PATHS = "\"BarChartExample-tvOS/Preview Content\""; 304 | DEVELOPMENT_TEAM = ""; 305 | ENABLE_PREVIEWS = YES; 306 | INFOPLIST_FILE = "BarChartExample-tvOS/Info.plist"; 307 | LD_RUNPATH_SEARCH_PATHS = ( 308 | "$(inherited)", 309 | "@executable_path/Frameworks", 310 | ); 311 | PRODUCT_BUNDLE_IDENTIFIER = "com.ByteKit.BarChartExample-tvOS"; 312 | PRODUCT_NAME = "$(TARGET_NAME)"; 313 | PROVISIONING_PROFILE_SPECIFIER = ""; 314 | SWIFT_VERSION = 5.0; 315 | TARGETED_DEVICE_FAMILY = 3; 316 | }; 317 | name = Debug; 318 | }; 319 | D317957D24C1B27F00DC2168 /* Release */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; 323 | CODE_SIGN_STYLE = Manual; 324 | DEVELOPMENT_ASSET_PATHS = "\"BarChartExample-tvOS/Preview Content\""; 325 | DEVELOPMENT_TEAM = ""; 326 | ENABLE_PREVIEWS = YES; 327 | INFOPLIST_FILE = "BarChartExample-tvOS/Info.plist"; 328 | LD_RUNPATH_SEARCH_PATHS = ( 329 | "$(inherited)", 330 | "@executable_path/Frameworks", 331 | ); 332 | PRODUCT_BUNDLE_IDENTIFIER = "com.ByteKit.BarChartExample-tvOS"; 333 | PRODUCT_NAME = "$(TARGET_NAME)"; 334 | PROVISIONING_PROFILE_SPECIFIER = ""; 335 | SWIFT_VERSION = 5.0; 336 | TARGETED_DEVICE_FAMILY = 3; 337 | }; 338 | name = Release; 339 | }; 340 | /* End XCBuildConfiguration section */ 341 | 342 | /* Begin XCConfigurationList section */ 343 | D317956424C1B27F00DC2168 /* Build configuration list for PBXProject "BarChartExample-tvOS" */ = { 344 | isa = XCConfigurationList; 345 | buildConfigurations = ( 346 | D317957924C1B27F00DC2168 /* Debug */, 347 | D317957A24C1B27F00DC2168 /* Release */, 348 | ); 349 | defaultConfigurationIsVisible = 0; 350 | defaultConfigurationName = Release; 351 | }; 352 | D317957B24C1B27F00DC2168 /* Build configuration list for PBXNativeTarget "BarChartExample-tvOS" */ = { 353 | isa = XCConfigurationList; 354 | buildConfigurations = ( 355 | D317957C24C1B27F00DC2168 /* Debug */, 356 | D317957D24C1B27F00DC2168 /* Release */, 357 | ); 358 | defaultConfigurationIsVisible = 0; 359 | defaultConfigurationName = Release; 360 | }; 361 | /* End XCConfigurationList section */ 362 | 363 | /* Begin XCSwiftPackageProductDependency section */ 364 | D399CAEF265C7F1A00A92054 /* BarChart */ = { 365 | isa = XCSwiftPackageProductDependency; 366 | productName = BarChart; 367 | }; 368 | /* End XCSwiftPackageProductDependency section */ 369 | }; 370 | rootObject = D317956124C1B27F00DC2168 /* Project object */; 371 | } 372 | -------------------------------------------------------------------------------- /Examples/BarChartExample-macOS/BarChartExample-macOS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D317954B24C1ADDF00DC2168 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D317954A24C1ADDF00DC2168 /* AppDelegate.swift */; }; 11 | D317954D24C1ADDF00DC2168 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D317954C24C1ADDF00DC2168 /* ContentView.swift */; }; 12 | D317954F24C1ADE000DC2168 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D317954E24C1ADE000DC2168 /* Assets.xcassets */; }; 13 | D317955224C1ADE000DC2168 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D317955124C1ADE000DC2168 /* Preview Assets.xcassets */; }; 14 | D317955524C1ADE000DC2168 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D317955324C1ADE000DC2168 /* Main.storyboard */; }; 15 | D399CAEA265C7E6900A92054 /* BarChart in Frameworks */ = {isa = PBXBuildFile; productRef = D399CAE9265C7E6900A92054 /* BarChart */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | D317954724C1ADDF00DC2168 /* BarChartExample-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "BarChartExample-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | D317954A24C1ADDF00DC2168 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | D317954C24C1ADDF00DC2168 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 22 | D317954E24C1ADE000DC2168 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 23 | D317955124C1ADE000DC2168 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 24 | D317955424C1ADE000DC2168 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 25 | D317955624C1ADE000DC2168 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | D317955724C1ADE000DC2168 /* BarChartExample_macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BarChartExample_macOS.entitlements; sourceTree = ""; }; 27 | D399CAE7265C7E5D00A92054 /* BarChart */ = {isa = PBXFileReference; lastKnownFileType = folder; name = BarChart; path = ../..; sourceTree = ""; }; 28 | /* End PBXFileReference section */ 29 | 30 | /* Begin PBXFrameworksBuildPhase section */ 31 | D317954424C1ADDF00DC2168 /* Frameworks */ = { 32 | isa = PBXFrameworksBuildPhase; 33 | buildActionMask = 2147483647; 34 | files = ( 35 | D399CAEA265C7E6900A92054 /* BarChart in Frameworks */, 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | D317953E24C1ADDF00DC2168 = { 43 | isa = PBXGroup; 44 | children = ( 45 | D399CAE7265C7E5D00A92054 /* BarChart */, 46 | D317954924C1ADDF00DC2168 /* BarChartExample-macOS */, 47 | D317954824C1ADDF00DC2168 /* Products */, 48 | D399CAE8265C7E6900A92054 /* Frameworks */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | D317954824C1ADDF00DC2168 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | D317954724C1ADDF00DC2168 /* BarChartExample-macOS.app */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | D317954924C1ADDF00DC2168 /* BarChartExample-macOS */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | D317954A24C1ADDF00DC2168 /* AppDelegate.swift */, 64 | D317954C24C1ADDF00DC2168 /* ContentView.swift */, 65 | D317954E24C1ADE000DC2168 /* Assets.xcassets */, 66 | D317955324C1ADE000DC2168 /* Main.storyboard */, 67 | D317955624C1ADE000DC2168 /* Info.plist */, 68 | D317955724C1ADE000DC2168 /* BarChartExample_macOS.entitlements */, 69 | D317955024C1ADE000DC2168 /* Preview Content */, 70 | ); 71 | path = "BarChartExample-macOS"; 72 | sourceTree = ""; 73 | }; 74 | D317955024C1ADE000DC2168 /* Preview Content */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | D317955124C1ADE000DC2168 /* Preview Assets.xcassets */, 78 | ); 79 | path = "Preview Content"; 80 | sourceTree = ""; 81 | }; 82 | D399CAE8265C7E6900A92054 /* Frameworks */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | ); 86 | name = Frameworks; 87 | sourceTree = ""; 88 | }; 89 | /* End PBXGroup section */ 90 | 91 | /* Begin PBXNativeTarget section */ 92 | D317954624C1ADDF00DC2168 /* BarChartExample-macOS */ = { 93 | isa = PBXNativeTarget; 94 | buildConfigurationList = D317955A24C1ADE000DC2168 /* Build configuration list for PBXNativeTarget "BarChartExample-macOS" */; 95 | buildPhases = ( 96 | D317954324C1ADDF00DC2168 /* Sources */, 97 | D317954424C1ADDF00DC2168 /* Frameworks */, 98 | D317954524C1ADDF00DC2168 /* Resources */, 99 | ); 100 | buildRules = ( 101 | ); 102 | dependencies = ( 103 | ); 104 | name = "BarChartExample-macOS"; 105 | packageProductDependencies = ( 106 | D399CAE9265C7E6900A92054 /* BarChart */, 107 | ); 108 | productName = "BarChartExample-macOS"; 109 | productReference = D317954724C1ADDF00DC2168 /* BarChartExample-macOS.app */; 110 | productType = "com.apple.product-type.application"; 111 | }; 112 | /* End PBXNativeTarget section */ 113 | 114 | /* Begin PBXProject section */ 115 | D317953F24C1ADDF00DC2168 /* Project object */ = { 116 | isa = PBXProject; 117 | attributes = { 118 | LastSwiftUpdateCheck = 1160; 119 | LastUpgradeCheck = 1240; 120 | ORGANIZATIONNAME = "Roman Baitaliuk"; 121 | TargetAttributes = { 122 | D317954624C1ADDF00DC2168 = { 123 | CreatedOnToolsVersion = 11.6; 124 | }; 125 | }; 126 | }; 127 | buildConfigurationList = D317954224C1ADDF00DC2168 /* Build configuration list for PBXProject "BarChartExample-macOS" */; 128 | compatibilityVersion = "Xcode 9.3"; 129 | developmentRegion = en; 130 | hasScannedForEncodings = 0; 131 | knownRegions = ( 132 | en, 133 | Base, 134 | ); 135 | mainGroup = D317953E24C1ADDF00DC2168; 136 | packageReferences = ( 137 | ); 138 | productRefGroup = D317954824C1ADDF00DC2168 /* Products */; 139 | projectDirPath = ""; 140 | projectRoot = ""; 141 | targets = ( 142 | D317954624C1ADDF00DC2168 /* BarChartExample-macOS */, 143 | ); 144 | }; 145 | /* End PBXProject section */ 146 | 147 | /* Begin PBXResourcesBuildPhase section */ 148 | D317954524C1ADDF00DC2168 /* Resources */ = { 149 | isa = PBXResourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | D317955524C1ADE000DC2168 /* Main.storyboard in Resources */, 153 | D317955224C1ADE000DC2168 /* Preview Assets.xcassets in Resources */, 154 | D317954F24C1ADE000DC2168 /* Assets.xcassets in Resources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXResourcesBuildPhase section */ 159 | 160 | /* Begin PBXSourcesBuildPhase section */ 161 | D317954324C1ADDF00DC2168 /* Sources */ = { 162 | isa = PBXSourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | D317954D24C1ADDF00DC2168 /* ContentView.swift in Sources */, 166 | D317954B24C1ADDF00DC2168 /* AppDelegate.swift in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXSourcesBuildPhase section */ 171 | 172 | /* Begin PBXVariantGroup section */ 173 | D317955324C1ADE000DC2168 /* Main.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | D317955424C1ADE000DC2168 /* Base */, 177 | ); 178 | name = Main.storyboard; 179 | sourceTree = ""; 180 | }; 181 | /* End PBXVariantGroup section */ 182 | 183 | /* Begin XCBuildConfiguration section */ 184 | D317955824C1ADE000DC2168 /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_ANALYZER_NONNULL = YES; 189 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 190 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 191 | CLANG_CXX_LIBRARY = "libc++"; 192 | CLANG_ENABLE_MODULES = YES; 193 | CLANG_ENABLE_OBJC_ARC = YES; 194 | CLANG_ENABLE_OBJC_WEAK = YES; 195 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 196 | CLANG_WARN_BOOL_CONVERSION = YES; 197 | CLANG_WARN_COMMA = YES; 198 | CLANG_WARN_CONSTANT_CONVERSION = YES; 199 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 211 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 212 | CLANG_WARN_STRICT_PROTOTYPES = YES; 213 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 214 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 215 | CLANG_WARN_UNREACHABLE_CODE = YES; 216 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 217 | COPY_PHASE_STRIP = NO; 218 | DEBUG_INFORMATION_FORMAT = dwarf; 219 | ENABLE_STRICT_OBJC_MSGSEND = YES; 220 | ENABLE_TESTABILITY = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu11; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_NO_COMMON_BLOCKS = YES; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | MACOSX_DEPLOYMENT_TARGET = 10.15; 236 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 237 | MTL_FAST_MATH = YES; 238 | ONLY_ACTIVE_ARCH = YES; 239 | SDKROOT = macosx; 240 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 241 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 242 | }; 243 | name = Debug; 244 | }; 245 | D317955924C1ADE000DC2168 /* Release */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | ALWAYS_SEARCH_USER_PATHS = NO; 249 | CLANG_ANALYZER_NONNULL = YES; 250 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 251 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 252 | CLANG_CXX_LIBRARY = "libc++"; 253 | CLANG_ENABLE_MODULES = YES; 254 | CLANG_ENABLE_OBJC_ARC = YES; 255 | CLANG_ENABLE_OBJC_WEAK = YES; 256 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 257 | CLANG_WARN_BOOL_CONVERSION = YES; 258 | CLANG_WARN_COMMA = YES; 259 | CLANG_WARN_CONSTANT_CONVERSION = YES; 260 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 261 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 262 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 263 | CLANG_WARN_EMPTY_BODY = YES; 264 | CLANG_WARN_ENUM_CONVERSION = YES; 265 | CLANG_WARN_INFINITE_RECURSION = YES; 266 | CLANG_WARN_INT_CONVERSION = YES; 267 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 269 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 271 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 272 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 273 | CLANG_WARN_STRICT_PROTOTYPES = YES; 274 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 275 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 276 | CLANG_WARN_UNREACHABLE_CODE = YES; 277 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 278 | COPY_PHASE_STRIP = NO; 279 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 280 | ENABLE_NS_ASSERTIONS = NO; 281 | ENABLE_STRICT_OBJC_MSGSEND = YES; 282 | GCC_C_LANGUAGE_STANDARD = gnu11; 283 | GCC_NO_COMMON_BLOCKS = YES; 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 286 | GCC_WARN_UNDECLARED_SELECTOR = YES; 287 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 288 | GCC_WARN_UNUSED_FUNCTION = YES; 289 | GCC_WARN_UNUSED_VARIABLE = YES; 290 | MACOSX_DEPLOYMENT_TARGET = 10.15; 291 | MTL_ENABLE_DEBUG_INFO = NO; 292 | MTL_FAST_MATH = YES; 293 | SDKROOT = macosx; 294 | SWIFT_COMPILATION_MODE = wholemodule; 295 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 296 | }; 297 | name = Release; 298 | }; 299 | D317955B24C1ADE000DC2168 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 303 | CODE_SIGN_ENTITLEMENTS = "BarChartExample-macOS/BarChartExample_macOS.entitlements"; 304 | CODE_SIGN_IDENTITY = "-"; 305 | CODE_SIGN_STYLE = Manual; 306 | COMBINE_HIDPI_IMAGES = YES; 307 | DEVELOPMENT_ASSET_PATHS = "\"BarChartExample-macOS/Preview Content\""; 308 | DEVELOPMENT_TEAM = ""; 309 | ENABLE_HARDENED_RUNTIME = YES; 310 | ENABLE_PREVIEWS = YES; 311 | INFOPLIST_FILE = "BarChartExample-macOS/Info.plist"; 312 | LD_RUNPATH_SEARCH_PATHS = ( 313 | "$(inherited)", 314 | "@executable_path/../Frameworks", 315 | ); 316 | MACOSX_DEPLOYMENT_TARGET = 10.15; 317 | PRODUCT_BUNDLE_IDENTIFIER = "com.ByteKit.BarChartExample-macOS"; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | PROVISIONING_PROFILE_SPECIFIER = ""; 320 | SWIFT_VERSION = 5.0; 321 | }; 322 | name = Debug; 323 | }; 324 | D317955C24C1ADE000DC2168 /* Release */ = { 325 | isa = XCBuildConfiguration; 326 | buildSettings = { 327 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 328 | CODE_SIGN_ENTITLEMENTS = "BarChartExample-macOS/BarChartExample_macOS.entitlements"; 329 | CODE_SIGN_IDENTITY = "-"; 330 | CODE_SIGN_STYLE = Manual; 331 | COMBINE_HIDPI_IMAGES = YES; 332 | DEVELOPMENT_ASSET_PATHS = "\"BarChartExample-macOS/Preview Content\""; 333 | DEVELOPMENT_TEAM = ""; 334 | ENABLE_HARDENED_RUNTIME = YES; 335 | ENABLE_PREVIEWS = YES; 336 | INFOPLIST_FILE = "BarChartExample-macOS/Info.plist"; 337 | LD_RUNPATH_SEARCH_PATHS = ( 338 | "$(inherited)", 339 | "@executable_path/../Frameworks", 340 | ); 341 | MACOSX_DEPLOYMENT_TARGET = 10.15; 342 | PRODUCT_BUNDLE_IDENTIFIER = "com.ByteKit.BarChartExample-macOS"; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | PROVISIONING_PROFILE_SPECIFIER = ""; 345 | SWIFT_VERSION = 5.0; 346 | }; 347 | name = Release; 348 | }; 349 | /* End XCBuildConfiguration section */ 350 | 351 | /* Begin XCConfigurationList section */ 352 | D317954224C1ADDF00DC2168 /* Build configuration list for PBXProject "BarChartExample-macOS" */ = { 353 | isa = XCConfigurationList; 354 | buildConfigurations = ( 355 | D317955824C1ADE000DC2168 /* Debug */, 356 | D317955924C1ADE000DC2168 /* Release */, 357 | ); 358 | defaultConfigurationIsVisible = 0; 359 | defaultConfigurationName = Release; 360 | }; 361 | D317955A24C1ADE000DC2168 /* Build configuration list for PBXNativeTarget "BarChartExample-macOS" */ = { 362 | isa = XCConfigurationList; 363 | buildConfigurations = ( 364 | D317955B24C1ADE000DC2168 /* Debug */, 365 | D317955C24C1ADE000DC2168 /* Release */, 366 | ); 367 | defaultConfigurationIsVisible = 0; 368 | defaultConfigurationName = Release; 369 | }; 370 | /* End XCConfigurationList section */ 371 | 372 | /* Begin XCSwiftPackageProductDependency section */ 373 | D399CAE9265C7E6900A92054 /* BarChart */ = { 374 | isa = XCSwiftPackageProductDependency; 375 | productName = BarChart; 376 | }; 377 | /* End XCSwiftPackageProductDependency section */ 378 | }; 379 | rootObject = D317953F24C1ADDF00DC2168 /* Project object */; 380 | } 381 | --------------------------------------------------------------------------------