Open judge opened 1 year ago
This issue may need more information before it can be addressed. In particular, it will need a reliable Code Reproduction that demonstrates the issue.
Please see the Contributing Guide for how to create a Code Reproduction.
Thanks! Ionitron 💙
can you change the id to main-map
and let me know if the problem still exists?
const newMap = await GoogleMap.create({
id: 'main-map', // <--
forceCreate: true,
element: mapRef,
apiKey: apiKey,
config: {
center: {
lat: 33.6,
lng: -117.9,
},
zoom: 8,
},
});
I was able to render map on ios using this snippet
Hi @DwieDima , I cannot see the map after the proposed change. :( I uploaded a sample application: https://github.com/judge/sample-app Thanks!
Hi @judge ,
Did you found any work around for this issue?
Unfortunetaly not. I would add "needs reply" and remove "needs reproduction" labels to the issue but I cannot do that. :(
Hi @judge ,
Did you enabled Maps SDK for iOS ? https://developers.google.com/maps/documentation/ios-sdk/cloud-setup#enabling-apis
Hi @akeeee ,
Yes it is enabled for both JavaScript and iOS. The JavaScript API works perfectly, it even shows request count but there is no request count for iOS.
It seems that the ScrollView the plugin is searching for is sometimes not existing yet when the map is initialized. I worked around this by changing Map.swift like this:
if let target = self.targetViewController {
target.tag = 1
target.removeAllSubview()
self.mapViewController.view.frame = target.bounds
target.addSubview(self.mapViewController.view)
self.mapViewController.GMapView.delegate = self.delegate
} else { // add this else case
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
self.render(callback)
}
return;
}
If you also need your callbacks to wait until the map is created you need to call the resolve at the point that onMapReady is signaled.
I'm also facing this issue exactly.
On first use of the map, the "box" and the marker loads, but there's no actual map.
If I navigate to another screen — even if that screen is using maps — I get nothing.
It does work on web; I get the same logs @judge is getting from XCode.
Using:
💊 Capacitor Doctor 💊
Latest Dependencies:
@capacitor/cli: 4.6.3 @capacitor/core: 4.6.3 @capacitor/android: 4.6.3 @capacitor/ios: 4.6.3
Installed Dependencies:
@capacitor/android: not installed @capacitor/cli: 4.6.3 @capacitor/core: 4.6.3 @capacitor/ios: 4.6.3
@judge where you able to find a solution?
@Simon54 thank you - that sorted it out for me. I'll now have to make sure this "patch" gets applied when this is deployed to CI.
Bump!
Ok... I think I found a better solution than patching Map.swift
, which was a hit-or-miss solution anyway.
What I'm doing and is consistently working is:
capacitor-google-map
element in a relative div:<template>
<div class="map-wrapper">
<capacitor-google-map id="map"></capacitor-google-map>
</div>
</template>
capacitor-google-map
an absolutely positioned element:<style scoped>
.map-wrapper {
position: relative;
flex: 1;
}
capacitor-google-map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
(I use this component within another that uses display: flex;
so I have flex: 1
to ensure the wrapper grows to maximum height on mobile)
capacitor-google-map
to be connected with a simple setTimeout
... AND IT WORKS EVERY TIME:<script setup lang="ts">
import { GoogleMap } from '@capacitor/google-maps';
import { onBeforeUnmount } from 'vue';
import { onMounted } from 'vue';
const apiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY;
let map: GoogleMap | null = null;
onMounted(async () => {
await new Promise(res => setTimeout(res, 300));
const mapRef = document.getElementById('map')!;
const newMap = await GoogleMap.create({
id: `my-map`, // Unique identifier for this map instance
element: mapRef!, // reference to the capacitor-google-map element
apiKey: apiKey, // Your Google Maps API Key
config: {
center: { lat: 33.6, lng: -117.9 },
zoom: 8,
},
});
map = newMap;
});
onBeforeUnmount(() => {
if (map) {
map.destroy();
map = null;
}
})
I did so many... many, many, many iterations with just a single change and this is the only solution without any changes to the plugin itself that worked!
Key findings:
capacitor-google-map
is a Custom Element, which takes a tiny bit of time to be hooked to its JS element after the <capacitor-google-map />
element is in the DOM. That JS connection adds an additional DIV element inside it that has 200% height, so this makes a ScollView in the WebKit browser.Map.swift
file makes a lookup for the said ScrollView by using some dodgy logic, but it works if everything is just right.capacitor-google-map
element must not be a Flex box and it must allow overflowing - otherwise the ScrollView won't be created by WebKit. Even if some parent container is a Flex box - the immediate parent should allow overflowing, so the 200% high DIV can ensure the ScrollView is created! So... your best bet is to use an absolutely positioned style for the bloody capacitor-google-map
and wrap it in a relatively positioned DIV, which you can size as your heart desires.That's it form me and good luck.
@judge I know this finding is some five months late, but to make my solution work in your context, since I use Vue and you do not - add a timeout before calling GoogleMap.create
to give a chance to capacitor-google-map
to connect properly.
I personally wasn't able to make it work with the styles suggested in the official "documentation" - I had to use a wrapper DIV, so in your case I would suggest you do:
this.innerHTML = `
<style>
#map-wrapper {
display: inline-block;
width: 200px;
height: 400px;
position: relative;
}
capacitor-google-map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
<div id="map-wrapper" style="border: 1px solid red;">
<capacitor-google-map id="main-map"></capacitor-google-map>
</div>
`;
I tested the above wrapper styles within my Vue app and it worked for me, but I had to move the red border to the wrapper - otherwise the dodgy logic in Maps.swift
fails to identify the ScrollView :)
It seems that the ScrollView the plugin is searching for is sometimes not existing yet when the map is initialized. I worked around this by changing Map.swift like this:
if let target = self.targetViewController { target.tag = 1 target.removeAllSubview() self.mapViewController.view.frame = target.bounds target.addSubview(self.mapViewController.view) self.mapViewController.GMapView.delegate = self.delegate } else { // add this else case DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { self.render(callback) } return; }
If you also need your callbacks to wait until the map is created you need to call the resolve at the point that onMapReady is signaled.
Can you give full example please?
@metinjakupi The <capacitor-google-map>
element is a custom element, which requires an event-loop cycle for the browser to connect it to JavaScript once the DOM is updated. Once that's connected and if the *ScrollView
exists the lookup for the targetViewController
will succeed. Otherwise it may need a bit more time.
Unfortunately if the *ScrollView
is never created by Webkit - that loop will go forever.
I did patch the swift file in the hope that would be enough of a fix, but it wasn't and I felt it is pretty bad - patching and all.
https://github.com/ionic-team/capacitor-plugins/issues/1366#issuecomment-1427893963
I think is actually along the correct lines of what the fix should be. The issue is that the web view is loaded before the map view component is even available, which confuses the JS code that executes after. Any subsequent calls into the Google Maps component will 100% yield a null reference exception because the map controller is simply not initialized. This completely screams lifecycle issue, to me, honestly.
Currently the code just invokes the resolve callback without any concern for if the map view controller is active or not. It just assumes so. The reason why the fix I linked above works is because it creates effectively a spinlock which ensures that the map view is always initialized before proceeding with invoking the resolve callback.
I'm honestly not sure how the Google Maps plugin shipped in 5.0 considering how easy it is to reproduce this bug.
any solution for this?
I believe the latest alpha version of the plugin does address the delay requirement, but I haven't tested it:
To be honest - I had to switch to a JS version for mapping, since this plugin proved to be less useful in several cases that we needed - like annotations (aka popups), marker SVG images and marker animation. I had to use Leaflet (+MapBox for tiles), since GoogleMaps JS requires a realtime download to import their JS libraries, which is not something we wanted. It was also such a nuisance to setup and use - no Vue (or any JS framework) support at all - just bare-bone procedural JS - a loaded foot-gun on every corner.
Still not a fix? I am tired of trying everyhting and couldn't make it load. Only the google logo is loading inside a gray area. :(
I believe the latest alpha version of the plugin does address the delay requirement, but I haven't tested it:
To be honest - I had to switch to a JS version for mapping, since this plugin proved to be less useful in several cases that we needed - like annotations (aka popups), marker SVG images and marker animation. I had to use Leaflet (+MapBox for tiles), since GoogleMaps JS requires a realtime download to import their JS libraries, which is not something we wanted. It was also such a nuisance to setup and use - no Vue (or any JS framework) support at all - just bare-bone procedural JS - a loaded foot-gun on every corner.
This doesn't fix the issue - because the issue is that the native code seems to fire off before the page is fully ready to have the map container injected/overlayed, hence why the fix suggested by https://github.com/ionic-team/capacitor-plugins/issues/1366#issuecomment-1427893963 works so well.
Particularly this bit of code:
else { // add this else case
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
self.render(callback)
}
return;
}
Though, I do not believe this to be the actual fix as I believe this is merely pushing the chance of this bug happening to a condition that's pretty much never going to happen. I think the issue is that some of the code for the Map Controller just simply executes too early.
@judge I know this finding is some five months late, but to make my solution work in your context, since I use Vue and you do not - add a timeout before calling
GoogleMap.create
to give a chance tocapacitor-google-map
to connect properly.I personally wasn't able to make it work with the styles suggested in the official "documentation" - I had to use a wrapper DIV, so in your case I would suggest you do:
this.innerHTML = ` <style> #map-wrapper { display: inline-block; width: 200px; height: 400px; position: relative; } capacitor-google-map { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } </style> <div id="map-wrapper" style="border: 1px solid red;"> <capacitor-google-map id="main-map"></capacitor-google-map> </div> `;
I tested the above wrapper styles within my Vue app and it worked for me, but I had to move the red border to the wrapper - otherwise the dodgy logic in
Maps.swift
fails to identify the ScrollView :)
I think this was a good hint. I spent some time now debugging some issues of this plugin but I can also now create the map with this help. I used this styling for my map which did the fix for me. It worked without a wrapper. I noticed that height: 99.9vh
did not work on some devices. I used e.g. an iPhone SE (1gen) with IOS 14, or IOS 15. Only if I change the height with a difference of 10vh
of the fullheight, it worked. When I use height: 99vh
it didn't work. No idea why... - maybe it depends on the device height.
capacitor-google-map {
display: inline-block;
width: 100vw;
height: 115vh;
top: 0;
left: 0;
right: 0;
bottom: 0;
position: fixed !important;
z-index: 1;
}
A workaround might be to render the map with a styling of 200vh and when the map is initialised to reduce it again to 100vh.
EDIT:
I noticed that after page transitions, the map doesn't reappear anymore... (with the fix with the styling)
@ngmiduc does it work with the fix with the styling, or with @gm112 suggestion using the spinlock, or both of them are needed?
@ngmiduc does it work with the fix with the styling, or with @gm112 suggestion using the spinlock, or both of them are needed?
I have tested the solution of @gm112 but I didn't resolve my issue. I added some extra logs and it seems to be an infinite loop where the reference to the google maps was lost by the ViewControler.
What works for me now is to set the width and height to 100vh and 100vw
. I used to have it at 99.9vh, but this worked on some devices but not on other devices. There is also no way to change the height dynamically. There are apparently some specific styling dimensions for some specific devices that will make the app disappear and page transitions will retrigger the native map function onDisplay
which could make the app disappear when the map has some bad heights or widths. That was at least my experience with the map.
@ngmiduc It seems that using this component is required for iOS: <capacitor-google-map id="map"></capacitor-google-map>
Before I was using a div, which worked fine on Android, and before that for some reason I tried using capacitor-google-maps
instead of capacitor-google-map
which didn't work. (Note I'm using Angular)
"The Google Maps Capacitor plugin ships with a web component that must be used to render the map in your application as it enables us to embed the native view more effectively on iOS" (from the readme) It looks like the component implements the style hack with the height.
I currently managed to get it shown, but only for a few seconds, until I'm starting to fill up the map, and for some reason it disappears (that might be related to something else in my project though)
I found out why the map disappeared. It seems to be related to dynamically adding a new element to the page where the map is created. Could someone explain why would that happen? Would the map binding break or how can this be avoided?
@yoaquim, I am also facing the exact same problem. Did you find any solution?
@ngmiduc does it work with the fix with the styling, or with @gm112 suggestion using the spinlock, or both of them are needed?
The spinlock issue I noted will propagate as null exceptions coming from the native plugin, which you would visually see as the Google Maps component not rendering at all. So regarding the styling fix, I believe people are having another issue that is unrelated. I hope that clears things up.
Bug Report
Plugin(s)
Google Maps
Capacitor Version
Platform(s)
iOS
Current Behavior
Empty box is appearing instead of Google Maps. Web works perfectly.
Expected Behavior
Show the map.
Code Reproduction
Sample webcomponent (apiKey deleted):
Other Technical Details
When I load the page on web it works fine, on iOS (physical device) I can see an empty box (with the red border I added).
Additional Context
There is no error at all, I can see the following in XCode: