diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo.xcodeproj/project.pbxproj b/GoogleMaps3Demo/GoogleMaps3DDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..01d0590 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo.xcodeproj/project.pbxproj @@ -0,0 +1,395 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 9DAEBE182DDCBB77002CC998 /* Config.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9DAEBE172DDCBB77002CC998 /* Config.xcconfig */; }; + 9DAEBE1D2DDCCB12002CC998 /* GoogleMaps3D in Frameworks */ = {isa = PBXBuildFile; productRef = 9DAEBE1C2DDCCB12002CC998 /* GoogleMaps3D */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9DCE6A1F2DC0F86500A76B23 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 9DAEBE172DDCBB77002CC998 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; + 9DCE69FC2DBFD50900A76B23 /* GoogleMaps3DDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GoogleMaps3DDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 9DAEBE1A2DDCBFEA002CC998 /* Exceptions for "GoogleMaps3DDemo" folder in "GoogleMaps3DDemo" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 9DCE69FB2DBFD50900A76B23 /* GoogleMaps3DDemo */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 9DCE69FE2DBFD50900A76B23 /* GoogleMaps3DDemo */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 9DAEBE1A2DDCBFEA002CC998 /* Exceptions for "GoogleMaps3DDemo" folder in "GoogleMaps3DDemo" target */, + ); + path = GoogleMaps3DDemo; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9DCE69F92DBFD50900A76B23 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9DAEBE1D2DDCCB12002CC998 /* GoogleMaps3D in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9DCE69F32DBFD50900A76B23 = { + isa = PBXGroup; + children = ( + 9DAEBE172DDCBB77002CC998 /* Config.xcconfig */, + 9DCE69FE2DBFD50900A76B23 /* GoogleMaps3DDemo */, + 9DCE69FD2DBFD50900A76B23 /* Products */, + ); + sourceTree = ""; + }; + 9DCE69FD2DBFD50900A76B23 /* Products */ = { + isa = PBXGroup; + children = ( + 9DCE69FC2DBFD50900A76B23 /* GoogleMaps3DDemo.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 9DCE69FB2DBFD50900A76B23 /* GoogleMaps3DDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9DCE6A0A2DBFD50B00A76B23 /* Build configuration list for PBXNativeTarget "GoogleMaps3DDemo" */; + buildPhases = ( + 9DCE69F82DBFD50900A76B23 /* Sources */, + 9DCE69F92DBFD50900A76B23 /* Frameworks */, + 9DCE69FA2DBFD50900A76B23 /* Resources */, + 9DCE6A1F2DC0F86500A76B23 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 9DCE69FE2DBFD50900A76B23 /* GoogleMaps3DDemo */, + ); + name = GoogleMaps3DDemo; + packageProductDependencies = ( + 9DAEBE1C2DDCCB12002CC998 /* GoogleMaps3D */, + ); + productName = GoogleMaps3D; + productReference = 9DCE69FC2DBFD50900A76B23 /* GoogleMaps3DDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 9DCE69F42DBFD50900A76B23 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + TargetAttributes = { + 9DCE69FB2DBFD50900A76B23 = { + CreatedOnToolsVersion = 16.0; + }; + }; + }; + buildConfigurationList = 9DCE69F72DBFD50900A76B23 /* Build configuration list for PBXProject "GoogleMaps3DDemo" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 9DCE69F32DBFD50900A76B23; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 9DAEBE1B2DDCCB12002CC998 /* XCRemoteSwiftPackageReference "ios-maps-3d-sdk" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 9DCE69FD2DBFD50900A76B23 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 9DCE69FB2DBFD50900A76B23 /* GoogleMaps3DDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 9DCE69FA2DBFD50900A76B23 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9DAEBE182DDCBB77002CC998 /* Config.xcconfig in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 9DCE69F82DBFD50900A76B23 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 9DCE6A082DBFD50B00A76B23 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9DAEBE172DDCBB77002CC998 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 9DCE6A092DBFD50B00A76B23 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 9DCE6A0B2DBFD50B00A76B23 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"GoogleMaps3DDemo/Preview Content\""; + DEVELOPMENT_TEAM = 683JHFZ83X; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = GoogleMaps3DDemo/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.google.GoogleMaps3DDemo.GoogleMaps3DDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 9DCE6A0C2DBFD50B00A76B23 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"GoogleMaps3DDemo/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = GoogleMaps3DDemo/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.GoogleMaps3DDemo.GoogleMaps3DDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 9DCE69F72DBFD50900A76B23 /* Build configuration list for PBXProject "GoogleMaps3DDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9DCE6A082DBFD50B00A76B23 /* Debug */, + 9DCE6A092DBFD50B00A76B23 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9DCE6A0A2DBFD50B00A76B23 /* Build configuration list for PBXNativeTarget "GoogleMaps3DDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9DCE6A0B2DBFD50B00A76B23 /* Debug */, + 9DCE6A0C2DBFD50B00A76B23 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 9DAEBE1B2DDCCB12002CC998 /* XCRemoteSwiftPackageReference "ios-maps-3d-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/googlemaps/ios-maps-3d-sdk"; + requirement = { + branch = main; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 9DAEBE1C2DDCCB12002CC998 /* GoogleMaps3D */ = { + isa = XCSwiftPackageProductDependency; + package = 9DAEBE1B2DDCCB12002CC998 /* XCRemoteSwiftPackageReference "ios-maps-3d-sdk" */; + productName = GoogleMaps3D; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 9DCE69F42DBFD50900A76B23 /* Project object */; +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/AccentColor.colorset/Contents.json b/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2305880 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/Contents.json b/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/CameraDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/CameraDemo.swift new file mode 100644 index 0000000..254e50c --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/CameraDemo.swift @@ -0,0 +1,47 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import GoogleMaps3D + +struct CameraDemo: View { + @State var camera: Camera = .sanFrancisco + @State private var animate: Bool = false + @State private var flyAround: Bool = false + + var body: some View { + VStack { + Map(camera: $camera, mode: .hybrid) + .flyCameraTo( + .seattle, + duration: 5, + trigger: animate, + completion: { flyAround = true } + ) + .flyCameraAround( + .seattle, + duration: 15, + rounds: 0.5, + trigger: flyAround + ) + Button("Start Flying") { + animate.toggle() + } + } + } +} + +#Preview { + CameraDemo() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/CameraRestrictionDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/CameraRestrictionDemo.swift new file mode 100644 index 0000000..968c6ed --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/CameraRestrictionDemo.swift @@ -0,0 +1,39 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import GoogleMaps3D +import SwiftUI + +struct CameraRestrictionDemo: View { + @State var camera:Camera = .init( + center: .init(latitude: 37.7905, longitude: -122.3989,) as LatLngAltitude, + heading: 279, + tilt: 71, + range: 2500 + ) + + var body: some View { + Map(camera: $camera, mode: .hybrid) + .cameraRestrictions([ + .bounds(.init(latitude: 37.7...37.8, longitude: (-122.4)...(-122.3))), + .altitude(1000...10000), + .heading(90...300), + .tilt(30...90) + ]) + } +} + +#Preview { + CameraRestrictionDemo() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/ContentView.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/ContentView.swift new file mode 100644 index 0000000..7105cc5 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/ContentView.swift @@ -0,0 +1,26 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import SwiftUI +import GoogleMaps3D + +struct ContentView: View { + var body: some View { + //minimum configuration 3D Map + Map(mode: .hybrid) + } +} + +#Preview { + ContentView() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/FlightDataLoader.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/FlightDataLoader.swift new file mode 100644 index 0000000..c49e03e --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/FlightDataLoader.swift @@ -0,0 +1,42 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import Foundation + +public class FlightDataLoader : ObservableObject { + + @Published var flightPathData: FlightPathData = FlightPathData(flight:[]) + @Published var isLoaded: Bool = false + + public init() { + load("flightpath.json") + } + + public func load(_ path: String) { + if let url = Bundle.main.url(forResource: path, withExtension: nil){ + if let data = try? Data(contentsOf: url){ + let jsondecoder = JSONDecoder() + do{ + let result = try jsondecoder.decode(FlightPathData.self, from: data) + flightPathData = result + isLoaded = true + } + catch { + print("Error trying to load or parse the JSON file.") + } + } + } + } +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/FlightPathData.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/FlightPathData.swift new file mode 100644 index 0000000..34c4ba7 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/FlightPathData.swift @@ -0,0 +1,29 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import GoogleMaps3D + +struct FlightPathData: Decodable { + let flight: [FlightPathLocation] +} + +struct FlightPathLocation: Decodable { + let timestamp: Int64 + let latitude: Double + let longitude: Double + let altitude: Double + let bearing: Double + let speed: Double +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/FlyAlongRouteDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/FlyAlongRouteDemo.swift new file mode 100644 index 0000000..5e476c8 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/FlyAlongRouteDemo.swift @@ -0,0 +1,78 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import GoogleMaps3D +import SwiftUI + +@MainActor +struct FlyAlongRouteDemo: View { + @State private var camera: Camera = .innsbruck + @State private var flyToDuration: TimeInterval = 5 + @State var animation: Bool = true + + @StateObject var flightData: FlightDataLoader = FlightDataLoader() + + var body: some View { + VStack { + if let first = flightData.flightPathData.flight.first { + Map(camera: $camera, mode: .hybrid) + .keyframeAnimator( + initialValue: makeCamera(step: first), + trigger: animation, + content: { view, value in + Map(camera: .constant(value), mode: .hybrid) + }, + keyframes: { _ in + KeyframeTrack { + let steps = flightData.flightPathData.flight + //if steps.count > 1 { + for i in 2.. CubicKeyframe { + return CubicKeyframe( + makeCamera(step: step), + duration: flyToDuration + ) + } + + func makeCamera(step: FlightPathLocation) -> Camera { + return .init( + center: .init(latitude: step.latitude, longitude: step.longitude, altitude: step.altitude), + heading: step.bearing, + tilt: 75, + roll: 0, + range: 200 + ) + } +} + +#Preview { + FlyAlongRouteDemo() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/GoogleMaps3DDemoApp.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/GoogleMaps3DDemoApp.swift new file mode 100644 index 0000000..6391e3d --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/GoogleMaps3DDemoApp.swift @@ -0,0 +1,83 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import GoogleMaps3D + +@main +struct GoogleMaps3DDemoApp: App { + var body: some Scene { + WindowGroup { + NavigationView { + List { + NavigationLink(destination: ContentView()) { + Text("Basic Map") + } + NavigationLink(destination: CameraDemo()) { + Text("Camera Demo") + } + NavigationLink(destination: CameraRestrictionDemo()) { + Text("Camera Restrictions Demo") + } + NavigationLink(destination: FlyAlongRouteDemo()) { + Text("Flight Path Demo") + } + NavigationLink(destination: MapTapDemo()) { + Text("Map Tap Demo") + } + NavigationLink(destination: MarkerDemo()) { + Text("Marker Demo") + } + NavigationLink(destination: MarkerCollisionDemo()) { + Text("Marker Collision Demo") + } + NavigationLink(destination: MarkerStyleDemo()) { + Text("Marker Style Demo") + } + NavigationLink(destination: ModelDemo()) { + Text("Model Demo") + } + NavigationLink(destination: PopoverDemo()) { + Text("Popover Demo") + } + NavigationLink(destination: ShapesDemo()) { + Text("Shapes Demo") + } + + + + } + } + + .onAppear { + /* + API Key Setup: + 1. Get an API key using the instructions at: https://developers.google.com/maps/documentation/maps-3d/ios-sdk/setup#create-project + 2. Create a .xcconfig file at the project root level + 3. Add this line: MAPS_API_KEY = your_api_key_here + 4. Replace "your_api_key_here" with the API key obtained in step 3 + + Note: Never commit your actual API key to source control + */ + guard let infoDictionary: [String: Any] = Bundle.main.infoDictionary else { + fatalError("Info.plist not found") + } + guard let apiKey: String = infoDictionary["MAPS_API_KEY"] as? String else { + fatalError("MAPS_API_KEY not set in Info.plist") + } + Map.apiKey = apiKey + } + } + } +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/Info.plist b/GoogleMaps3Demo/GoogleMaps3DDemo/Info.plist new file mode 100644 index 0000000..b6770fa --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/Info.plist @@ -0,0 +1,8 @@ + + + + + MAPS_API_KEY + $(MAPS_API_KEY) + + diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/JSON/flightpath.json b/GoogleMaps3Demo/GoogleMaps3DDemo/JSON/flightpath.json new file mode 100644 index 0000000..38107fb --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/JSON/flightpath.json @@ -0,0 +1,156 @@ +{ + "flight": [ + { + "timestamp": 1712018310000, + "latitude": 47.3844, + "longitude": 11.8412, + "altitude": 2872.74, + "bearing": 237, + "speed": 240.0 + }, + { + "timestamp": 1712018327000, + "latitude": 47.3755, + "longitude": 11.8185, + "altitude": 2857.5, + "bearing": 243, + "speed": 215.0 + }, + { + "timestamp": 1712018344000, + "latitude": 47.3687, + "longitude": 11.7966, + "altitude": 2743.20, + "bearing": 248, + "speed": 215.0 + }, + { + "timestamp": 1712018374000, + "latitude": 47.3573, + "longitude": 11.7554, + "altitude": 2545.08, + "bearing": 248, + "speed": 211.0 + }, + { + "timestamp": 1712018404000, + "latitude": 47.3464, + "longitude": 11.7159, + "altitude": 2346.96, + "bearing": 248, + "speed": 210.0 + }, + { + "timestamp": 1712018434000, + "latitude": 47.3353, + "longitude": 11.676, + "altitude": 2141.22, + "bearing": 247, + "speed": 200.0 + }, + { + "timestamp": 1712018464000, + "latitude": 47.325, + "longitude": 11.6385, + "altitude": 1958.34, + "bearing": 248, + "speed": 200.0 + }, + { + "timestamp": 1712018494000, + "latitude": 47.3141, + "longitude": 11.5999, + "altitude": 1767.84, + "bearing": 248, + "speed": 200.0 + }, + { + "timestamp": 1712018524000, + "latitude": 47.3031, + "longitude": 11.5603, + "altitude": 1569.72, + "bearing": 248, + "speed": 210.0 + }, + { + "timestamp": 1712018555000, + "latitude": 47.292, + "longitude": 11.5206, + "altitude": 1363.98, + "bearing": 248, + "speed": 210.0 + }, + { + "timestamp": 1712018585000, + "latitude": 47.2809, + "longitude": 11.4809, + "altitude": 1165.86, + "bearing": 247, + "speed": 180.0 + }, + { + "timestamp": 1712018601000, + "latitude": 47.2758, + "longitude": 11.4629, + "altitude": 1074.42, + "bearing": 248, + "speed": 170.0 + }, + { + "timestamp": 1712018616000, + "latitude": 47.2718, + "longitude": 11.4476, + "altitude": 998.22, + "bearing": 253, + "speed": 165.0 + }, + { + "timestamp": 1712018618000, + "latitude": 47.2715, + "longitude": 11.4459, + "altitude": 990.6, + "bearing": 254, + "speed": 160.0 + }, + { + "timestamp": 1712018635000, + "latitude": 47.2692, + "longitude": 11.428, + "altitude": 906.78, + "bearing": 261, + "speed": 150.0 + }, + { + "timestamp": 1712018651000, + "latitude": 47.2674, + "longitude": 11.4113, + "altitude": 830.58, + "bearing": 261, + "speed": 130.0 + }, + { + "timestamp": 1712018667000, + "latitude": 47.2662, + "longitude": 11.3993, + "altitude": 769.62, + "bearing": 261, + "speed": 124.0 + }, + { + "timestamp": 1712018685000, + "latitude": 47.2644, + "longitude": 11.3827, + "altitude": 693.42, + "bearing": 261, + "speed": 120.0 + }, + { + "timestamp": 1712018701000, + "latitude": 47.263, + "longitude": 11.3704, + "altitude": 640.08, + "bearing": 261, + "speed": 120.0 + } + ] +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/MapHelpers.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/MapHelpers.swift new file mode 100644 index 0000000..5da0bcf --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/MapHelpers.swift @@ -0,0 +1,71 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation +import GoogleMaps3D + + +extension Camera { + public static var sanFrancisco: Camera = .init( + center: .init(latitude: 37.7845812, longitude: -122.3660241, altitude: 585), + heading: 288.0, + tilt: 75.0, + roll: 0.0, + range: 100) + + public static var sanFranciscoZoomedOut: Camera = .init( + center: .init(latitude: 37.7845812, longitude: -122.3660241, altitude: 1585), + heading: 288.0, + tilt: 35.0, + roll: 0.0, + range: 40000) + + public static var seattle: Camera = .init( + center: .init(latitude: 47.6210296, longitude: -122.3496903) as LatLngAltitude, + heading: 149.0, + tilt: 77.0, + roll: 0.0, + range: 4000) + + public static var downtownSanFrancisco: Camera = .init( + center: .init(latitude: 37.7957734, longitude: -122.4000130) as LatLngAltitude, + heading: 25, + tilt: 20, + range: 4200) + + public static var innsbruck: Camera = .init( + center: .init(latitude: 47.263, longitude: 11.3704, altitude: 640.08), + heading: 237, + tilt: 80.0, + roll: 0.0, + range: 200) +} + +struct MapEntity: Identifiable { + var content: T + var id: String = UUID().uuidString +} + +extension LatLngAltitude { + public static let alcatrazIsland = LatLngAltitude( + latitude: 37.8267, + longitude: -122.4233, + altitude: 10 + ) + + public static let ferryBuilding = LatLngAltitude( + latitude: 37.7955, + longitude: -122.3937 + ) +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/MapTapDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/MapTapDemo.swift new file mode 100644 index 0000000..0a877ff --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/MapTapDemo.swift @@ -0,0 +1,48 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import GoogleMaps3D +import SwiftUI + +struct MapTapDemo: View { + @State var camera: Camera = .sanFrancisco + @State var isPresented = false + @State var tapInfo: MapTapInfo? + @State var alertMessage: String = "" + + var body: some View { + Map(camera: $camera, mode: .hybrid) + .onTap { tapInfo in + self.tapInfo = tapInfo + isPresented = true + switch tapInfo.content { + case .map: + alertMessage = "Map tapped" + case .place(let placeId): + alertMessage = "Place tapped: \(placeId)" + default: + alertMessage = "Unknown tap" + } + } + .alert( + alertMessage, + isPresented: $isPresented, + actions: { Button("OK") {} } + ) + } +} +#Preview { + MapTapDemo() +} + diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerCollisionDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerCollisionDemo.swift new file mode 100644 index 0000000..0b5f8ad --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerCollisionDemo.swift @@ -0,0 +1,68 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import GoogleMaps3D +import SwiftUI + +struct MarkerCollisionDemo: View { + @State var sanFrancisco: Camera = .init(center: .init(latitude: 37.79556672721526, longitude: -122.39345183225336, altitude: 50), heading: 226.0, tilt: 75.0, roll: 0.0, range: 500) + + + var body: some View { + VStack { + Map(camera: $sanFrancisco, mode: .hybrid) { + Marker( + position: .init( + latitude: 37.794724, + longitude: -122.39442, + altitude: 100.0), + altitudeMode: .relativeToMesh, + collisionBehavior: .optionalAndHidesLowerPriority, + extruded: true, + label: "Marker 1" + ) + Marker( + position: .init( + latitude: 37.7943220, + longitude: -122.395073, + altitude: 100.0), + altitudeMode: .relativeToMesh, + collisionBehavior: .required, + extruded: true, + label: "Marker 2" + ) + + Marker( + position: .init( + latitude: 37.7934108, + longitude: -122.396144, + altitude: 100.0), + altitudeMode: .relativeToMesh, + collisionBehavior: .requiredAndHidesOptional, + extruded: true, + label: "Marker 3" + ) + + } + + } + } +} + + + +#Preview { + MarkerCollisionDemo() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerDemo.swift new file mode 100644 index 0000000..ee87301 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerDemo.swift @@ -0,0 +1,60 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import GoogleMaps3D + +struct MarkerDemo: View { + @State var camera: Camera = .sanFrancisco + @State var mapMarker: Marker = .init( + position: .init( + latitude: 37.8044862, + longitude: -122.4301493, + altitude: 0.0), + altitudeMode: .absolute, + collisionBehavior: .required, + extruded: false, + drawsWhenOccluded: true, + sizePreserved: true, + zIndex: 0, + label: "Test" + ) + + @State var extrudedMarker: Marker = .init( + position: .init( + latitude: 37.78980534, + longitude: -122.3969349, + altitude: 50.0), + altitudeMode: .relativeToMesh, + collisionBehavior: .required, + extruded: true, + drawsWhenOccluded: true, + sizePreserved: true, + zIndex: 0, + label: "Extruded" + ) + + var body: some View { + VStack { + Map(camera: $camera, mode: .hybrid) { + mapMarker + extrudedMarker + } + } + } +} + +#Preview { + MarkerDemo() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerStyleDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerStyleDemo.swift new file mode 100644 index 0000000..6e0be1f --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/MarkerStyleDemo.swift @@ -0,0 +1,144 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import GoogleMaps3D +import SwiftUI + +struct MarkerStyleDemo: View { + @State var markers: [MapEntity] = [ + .init(content: randomMarker(camera: .sanFranciscoZoomedOut, style: .pin(.red))) + ] + + @State var isShowingAlert: Bool = false + @State var title: String = "" + + var body: some View { + VStack { + Map(initialCamera: .sanFranciscoZoomedOut, mode: .hybrid) { + ForEach(markers) { marker in + marker.content + .onTap { + title = "Marker tapped \(marker.id)" + isShowingAlert = true + } + } + } + HStack { + Spacer() + Button { + markers.append( + .init( + content: + randomMarker( + camera: .sanFranciscoZoomedOut, style: .pin(.red)))) + } label: { + Image(systemName: "pin.fill") + .foregroundColor(.red) + } + Spacer() + Button { + markers.append( + .init( + content: + randomMarker( + camera: .sanFranciscoZoomedOut, style: .pin(.init(backgroundColor: .yellow))))) + } label: { + Image(systemName: "pin.fill") + .foregroundColor(.yellow) + } + Spacer() + Button { + markers.append( + .init( + content: + randomMarker( + camera: .sanFranciscoZoomedOut, + style: .pin( + .init(backgroundColor: .green, borderColor: .brown) { + Text("G") + .minimumScaleFactor(0.01) + })))) + } label: { + Image(systemName: "text.bubble") + } + Spacer() + Button { + markers.append( + .init( + content: + randomMarker( + camera: .sanFranciscoZoomedOut, + style: .viewSnapshot { + Image(systemName: "plus") + .font(.system(size: 6)) + .foregroundStyle(.orange) + .padding(2) + .border(Color.orange, width: 1) + }))) + } label: { + Image(systemName: "puzzlepiece.extension") + } + Spacer() + Button { + markers.append( + .init( + content: + randomMarker(camera: .sanFranciscoZoomedOut, style: .image(Image(systemName: "plus"))))) + } label: { + Image(systemName: "photo") + } + Spacer() + Button { + markers.append( + .init( + content: + randomMarker( + camera: .sanFranciscoZoomedOut, style: .pin(.init(scale: 2.0))))) + } label: { + Image(systemName: "pin.fill") + .foregroundColor(.blue) + .scaleEffect(1.5) + } + Spacer() + } + .alert(isPresented: $isShowingAlert) { + Alert( + title: Text(title), + dismissButton: .default(Text("OK")) + ) + } + } + } +} + +@MainActor +private func randomMarker(camera: Camera, style: Marker.Style) -> Marker { + Marker( + position: .init( + latitude: camera.center.latitude + Double.random(in: 0...0.1) - 0.05, + longitude: camera.center.longitude + Double.random(in: 0...0.1) - 0.05, + altitude: 0.0), + altitudeMode: .absolute, + collisionBehavior: .required, + extruded: false, + drawsWhenOccluded: true, + sizePreserved: true, + zIndex: 0, + label: "Test", + style: style) +} + +#Preview { + MarkerStyleDemo() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/ModelDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/ModelDemo.swift new file mode 100644 index 0000000..eacdbf1 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/ModelDemo.swift @@ -0,0 +1,43 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import GoogleMaps3D + +struct ModelDemo: View { + @State var camera: Camera = .sanFrancisco + @State var balloonModel: Model = .init( + position: .init( + latitude: 37.791376, + longitude: -122.397571, + altitude: 300.0), + url: URL(fileURLWithPath: fileUrl?.relativePath ?? ""), + altitudeMode: .absolute, + scale: .init(x: 5, y: 5, z: 5), + orientation: .init(heading: 0, tilt: 0, roll: 0) + ) + + var body: some View { + VStack { + Map(camera: $camera, mode: .hybrid) { + balloonModel + } + } + } +} +private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb") + +#Preview { + ModelDemo() +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/Models/balloon.glb b/GoogleMaps3Demo/GoogleMaps3DDemo/Models/balloon.glb new file mode 100644 index 0000000..eaab5c6 Binary files /dev/null and b/GoogleMaps3Demo/GoogleMaps3DDemo/Models/balloon.glb differ diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/PopoverDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/PopoverDemo.swift new file mode 100644 index 0000000..b13d174 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/PopoverDemo.swift @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import GoogleMaps3D + +public struct PopoverDemo: View { + @State private var isCoordinateWindowOpen: Bool = false + @State private var isFerryBuildingMarkerPopoverOpen: Bool = false + private let ferryBuildingMarker = Marker(position: .ferryBuilding) + public var body: some View { + ZStack(alignment: .bottom) { + Map(initialCamera: .sanFrancisco, mode: .hybrid) { + Popover( + positionAnchor: .alcatrazIsland, + altitudeMode: .relativeToGround, + isOpen: $isCoordinateWindowOpen, + autoClosedEnabled: false, + ) { + VStack { + Text("Alcatraz Island").font(.headline) + Text("Anchored to a coordinate.") + } + } + .style( + PopoverStyle( + padding: 20, + backgroundColor: .blue, + borderRadius: 8, + shadow: Shadow(color: .blue.opacity(1), radius: 50, x: 0, y: 2) + ) + ) + ferryBuildingMarker + .popover( + isOpen: $isFerryBuildingMarkerPopoverOpen, + style: PopoverStyle( + padding: 20, + backgroundColor: .red, + borderRadius: 8 + ) + ) { + VStack { + Text("Ferry Building").font(.headline) + Text("Anchored to a marker.") + } + } + .onTap { + isFerryBuildingMarkerPopoverOpen.toggle() + } + } + VStack { + Toggle("Show Coordinate Window", isOn: $isCoordinateWindowOpen) + Toggle("Show Ferry Building Marker Popover", isOn: $isFerryBuildingMarkerPopoverOpen) + } + .padding() + .background(.ultraThinMaterial) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .padding() + } + } +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/Preview Content/Preview Assets.xcassets/Contents.json b/GoogleMaps3Demo/GoogleMaps3DDemo/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/GoogleMaps3Demo/GoogleMaps3DDemo/ShapesDemo.swift b/GoogleMaps3Demo/GoogleMaps3DDemo/ShapesDemo.swift new file mode 100644 index 0000000..02b49ea --- /dev/null +++ b/GoogleMaps3Demo/GoogleMaps3DDemo/ShapesDemo.swift @@ -0,0 +1,97 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI +import GoogleMaps3D + +struct ShapesDemo: View { + @State var camera: Camera = .downtownSanFrancisco + @State var isShowingAlert: Bool = false + @State var title: String = "" + + var body: some View { + VStack { + Map(camera: $camera, mode: .hybrid) { + polyline.onTap { + title = "Polyline tapped" + isShowingAlert = true + } + originPolygon.onTap { + title = "Origin polygon tapped" + isShowingAlert = true + } + destinationPolygon.onTap { + title = "Destination polygon tapped" + isShowingAlert = true + } + } + .alert(isPresented: $isShowingAlert) { + Alert( + title: Text(title), + dismissButton: .default(Text("OK")) + ) + } + } + } +} + +var polyline: Polyline = .init(path: [ + LatLngAltitude(latitude: 37.80515638571346, longitude: -122.4032569467164, altitude: 0), + LatLngAltitude(latitude: 37.80337073509504, longitude: -122.4012878349353, altitude: 0), + LatLngAltitude(latitude: 37.79925208843463, longitude: -122.3976697250461, altitude: 0), + LatLngAltitude(latitude: 37.7989102378512, longitude: -122.3983408725656, altitude: 0), + LatLngAltitude(latitude: 37.79887832784348, longitude: -122.3987094864192, altitude: 0), + LatLngAltitude(latitude: 37.79786443410338, longitude: -122.4066878788802, altitude: 0), + LatLngAltitude(latitude: 37.79549248916587, longitude: -122.4032992702785, altitude: 0), + LatLngAltitude(latitude: 37.78861484290265, longitude: -122.4019489189814, altitude: 0), + LatLngAltitude(latitude: 37.78618687561075, longitude: -122.398969592545, altitude: 0), + LatLngAltitude(latitude: 37.7892310309145, longitude: -122.3951458683092, altitude: 0), + LatLngAltitude(latitude: 37.7916358762409, longitude: -122.3981969390652, altitude: 0) + ]) + .stroke(GoogleMaps3D.Polyline.StrokeStyle( + strokeColor: UIColor(red: 0.09803921568627451, green: 0.403921568627451, blue: 0.8235294117647058, alpha: 1), + strokeWidth: 10.0, + outerColor: .white, + outerWidth: 0.2 + )) + .contour(GoogleMaps3D.Polyline.ContourStyle(geodesic: true)) + + +var originPolygon: Polygon = .init( + path: [ + LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300), + LatLngAltitude(latitude: 37.7915324439261, longitude: -122.3982171091383, altitude: 300), + LatLngAltitude(latitude: 37.79166617650914, longitude: -122.3980478493319, altitude: 300), + LatLngAltitude(latitude: 37.79178986470217, longitude: -122.3982041104199, altitude: 300), + LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300 ) + ], + altitudeMode: .relativeToGround +) +.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.green, extruded: true) ) + +var destinationPolygon: Polygon = .init( + path: [ + LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300), + LatLngAltitude(latitude: 37.80503794515428, longitude: -122.4032633416024, altitude: 300), + LatLngAltitude(latitude: 37.80517850164195, longitude: -122.4031056058006, altitude: 300), + LatLngAltitude(latitude: 37.80529346901115, longitude: -122.4032622466595, altitude: 300), + LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300 ) + ], + altitudeMode: .relativeToGround +) +.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.red, extruded: true) ) + +#Preview { + ShapesDemo() +}