{"id":13778485,"url":"https://github.com/grandsir/InteractiveMap","last_synced_at":"2025-05-11T12:30:36.238Z","repository":{"id":153591290,"uuid":"538568659","full_name":"grandsir/InteractiveMap","owner":"grandsir","description":"An Interactive Map Library for Swift and SwiftUI!","archived":false,"fork":false,"pushed_at":"2023-12-06T10:00:20.000Z","size":3170,"stargazers_count":86,"open_issues_count":4,"forks_count":11,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-08-03T18:12:55.644Z","etag":null,"topics":["interactive","interactive-map","ios","ios-swift","ipados","macos","map","mapbox","mapkit","mapping","maps","svg","swift","swift5","swiftui","swiftui1","swiftui2","swiftui3"],"latest_commit_sha":null,"homepage":"https://medium.com/better-programming/how-to-create-interactive-maps-using-swiftui-1c49732e2950","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grandsir.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-09-19T15:27:00.000Z","updated_at":"2024-08-03T12:56:57.000Z","dependencies_parsed_at":"2023-12-06T11:34:49.912Z","dependency_job_id":null,"html_url":"https://github.com/grandsir/InteractiveMap","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grandsir%2FInteractiveMap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grandsir%2FInteractiveMap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grandsir%2FInteractiveMap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grandsir%2FInteractiveMap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grandsir","download_url":"https://codeload.github.com/grandsir/InteractiveMap/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225048942,"owners_count":17412898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["interactive","interactive-map","ios","ios-swift","ipados","macos","map","mapbox","mapkit","mapping","maps","svg","swift","swift5","swiftui","swiftui1","swiftui2","swiftui3"],"created_at":"2024-08-03T18:00:54.142Z","updated_at":"2024-11-17T14:30:30.360Z","avatar_url":"https://github.com/grandsir.png","language":"Swift","readme":"![Interactive Map](./Assets/interactiveMap.png)\n# InteractiveMap\n\nA Library to use SVG Based Maps interactively in SwiftUI.\n\n- Works **only with** .svg based maps\n- Allows you to modify **all** the provinces in the map with the attributes that SwiftUI's `Shape` provides\n- Drag, drop and animate the provinces, as well as the map itself.\n\nNote! SVG Parsing is not yet in its final form, so some SVGs may not be parsed correctly. But as far as I can see almost every map is drawn correctly. You can see the maps I tried in the [Maps](./Maps) section.\n\nMaps are taken from [FSInteractiveMap](https://github.com/ArthurGuibert/FSInteractiveMap) Repository\n\n\u003ch3 style =\"text-align: center\"\u003eInstallation\u003c/h3\u003e \n\u003cp\u003eRequires \u003cb\u003eiOS 13+\u003c/b\u003e \n\nInteractiveMap currently can only be installed through the Swift Package Manager.\u003c/p\u003e\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cstrong\u003e\nSwift Package Manager\n\u003c/strong\u003e\n\u003cbr\u003e\nAdd the Package URL:\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\u003cbr\u003e\n    \n```\nhttps://github.com/GrandSir/InteractiveMap\n```\n    \n\u003c/td\u003e\n\u003c/table\u003e\n\n# Examples \u0026 ShowCase\n[3D Scaling Effect](./Examples/3DEffect.swift)\n\n![3d-map](https://user-images.githubusercontent.com/69051988/191280598-63e0f189-2391-4d3c-90c0-06b6ea8d72b3.gif)\n\n[Creepy Map](./Examples/creepyMap.swift)\n\n![creepy)](https://user-images.githubusercontent.com/69051988/191286548-91302f94-7b16-4427-82c5-503fe0a442e0.gif)\n\n[Mihmandar Province Selection Screen](./Examples/Mihmandar.swift)\n\n![mihmandar](https://user-images.githubusercontent.com/69051988/191287726-49ff6d5a-ce56-49be-a235-d0c03710b16f.gif)\n\n\u003ch2 style=\"text-align: center; padding: 10px\"\u003eUsage\u003c/h2\u003e\n\n# SwiftUI\nTo draw your svg map in SwiftUI, use `InteractiveMap` with a closure taking `PathData` as the parameter.\n\n`InteractiveMap` uses `InteractiveShape`s to draw all the paths defined in SVG. But it needs to know which `Path` will be drawn, that is, `InteractiveMap { pathData in }` works just like `ForEach { index in }` and returns an iterable closure returning `PathData` as parameter, which contains all the information about `Path`s defined inside svg.\n\n```swift\nimport SwiftUI\nimport InteractiveMap\n\nstruct ContentView: View {\n    var body: some View {\n        InteractiveMap(svgName: \"tr\") { pathData in // or just use $0\n            InteractiveShape(pathData)\n                .initWithAttributes()\n        }\n    }\n}\n```\nInteractiveMap resizes itself to the assigned frame, takes all available space by default, and resizes itself responsively on device rotations. \n\n\u003cimg src=\"Assets/map_default.png\" width=700 alt=\"Default Map\"\u003e\n\n# Customization\n\nInstead of using default attributes, you can define your own attributes as well. \n\n```swift\nInteractiveMap(svgName: \"tr\") {\n    InteractiveShape($0)\n        .initWithAttributes(.init(strokeWidth: 2, strokeColor: .red, background: Color(white: 0.2)))\n}\n```\n\u003cimg src=\"Assets/map_customized_with_attributes.png\" width=700 alt=\"Custom Attributes Map\"\u003e\n\n## Advanced Customization\n\nEven though `.initWithAttributes` saves time for simple customization, it is neither highly customizable nor editable.\n\nSince `InteractiveShape` is a `Shape`, you can use any methods with `InteractiveShape` that you can use with `Shape`.\n```swift\nInteractiveMap(svgName: \"tr\") {\n    InteractiveShape($0)\n        .stroke(Color.cyan)\n        .shadow(color: .cyan, radius: 3, x: 0, y: 0)\n        .background(InteractiveShape($0).fill(Color(white: 0.15)))\n}\n```\n\u003cimg src=\"Assets/map_shadow.png\" width=700 alt=\"Shadow Map\"\u003e\n\n### Handling Clicks\n`PathData` is a `Struct` that contains all the information about all the paths, in our map example, they are districts and provinces.\n\nIt has 5 variables inside it. `id`, `path` and `name`, `boundingBox` and `svgBounds`\n\n`id` is the Unique Identifier that's being parsed directly from SVG\n\nMost of the Map SVG's (NOT ALL!) has the `id` attribute in all in their `\u003cpath\u003e` element like this:\n\n`\u003cpath ... id=\"\u003cid\u003e\", name=\"\u003cname\u003e\"\u003e`\n\n`MapParser`, defined in `MapParser.swift` parses that element and stores them in `PathData` struct.\n\nIf there is not any `id` attribute in path, MapParser automatically creates an UUID String.\n\nBut if you're going to store that id in somewhere, be aware that UUID String is automaticaly regenerated every time InteractiveMap is being drawed.\n\n```swift\nimport SwiftUI\nimport InteractiveMap\n\nstruct ContentView: View {\n    @State private var clickedPath = PathData()\n    var body: some View {\n        VStack {\n            Text(clickedPath.name.isEmpty ? \"\" : \"\\(clickedPath.name) is clicked!\" )\n                .font(.largeTitle)\n                .padding(.bottom, 15)\n\n            InteractiveMap(svgName: \"tr\") { pathData in // is a PathData\n                InteractiveShape(pathData)\n                    .stroke(clickedPath == pathData ? .cyan : .red, lineWidth: 1)\n                    .shadow(color: clickedPath == pathData ? .cyan : .red,  radius: 3)\n                    .shadow(color: clickedPath == pathData ? .cyan : .clear , radius: 3) // to increase the glow amount\n                    .background(InteractiveShape(pathData).fill(Color(white: 0.15))) // filling the shapes\n                    .shadow(color: clickedPath == pathData ? .black : .clear , radius: 5, y: 1) // for depth\n\n                    .onTapGesture {\n                        clickedPath = pathData\n                    }\n                    .zIndex(clickedPath == pathData ? 2 : 1) // this is REQUIRED because InteractiveShapes overlap, resulting in an ugly appearance\n                    .animation(.easeInOut(duration: 0.3), value: clickedPath)\n            }\n        }\n    }\n}\n```\n`clickedPath == pathData` basically compares the id's of the PathDatas.\n\n![clickable-map](https://user-images.githubusercontent.com/69051988/191283653-00cd4ebe-5ccd-48a1-b55b-3f70edfa3b14.gif)\n\n# UIKit\n\nsoon\n\n# SpriteKit\n\nsoon\n","funding_links":[],"categories":["Map"],"sub_categories":["Content"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrandsir%2FInteractiveMap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrandsir%2FInteractiveMap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrandsir%2FInteractiveMap/lists"}