I have an iOS application which displays a map using the Google Maps Javascript API in a UIWebView. It seems that in iOS 7.0, something has changed in the UIWebView implementation that is causing map objects to not be released when there are no longer any references.
Using the XCode memory profiler, I see a 7-20 MB increase in RAM each time a map is created. Removing the map, and clearing references to it does not decrease the memory usage. Memory warnings do not seem to trigger Javascript garbage collection. When running the code on a physical device, the application logs memory warnings and inevitably will crash.
Here is some simplified Javascript which demonstrates the problem:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title></title>
<style type="text/css" media="screen">
body { padding-top: 20px;}
#container { width:100%; height:200px; }
#container > div { height: 100%; width: 100%; }
</style>
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" charset="utf-8">
var domEl;
function createMap() {
var container = document.getElementById( 'container' );
// Create fresh dom element
//
if( domEl ) {
container.removeChild( domEl );
console.log('removed old dom element');
}
domEl = document.createElement( 'div' );
container.appendChild( domEl );
// Create map
//
var mapOptions = {
center: new google.maps.LatLng( -34.397, 150.644 ),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
new google.maps.Map( domEl, mapOptions );
return false;
}
</script>
</head>
<body>
Create map
<div id="container"></div>
</body>
</html>
I've also posted a complete XCode project on GitHub.
Has anyone seen this before? Can someone suggest a fix or workaround? In this case I have to use the Google Maps API; a native solution isn't an option.
Related
I have a nice Django app that implements Auth0. It works on all browsers on pcs and on browsers on Android. When testing on iOS devices however, after the user logs in through Auth0, the device asks to download a file and then downloads it and does nothing. If I try to redirect to my english page, it downloads a file called "en", if I try to redirect to my french version of the page, it downloads a file called "fr". Not sure why - it is at the end of the url myurl.something.org/myForm/en for English for example.
At first I thought the issue had to do with Apple not allowing Same-Site cookies, so I added the CSRF_COOKIE_SAMESITE = None setting. But I see now that after logging in, in the address bar there is the url that I want the user to be redirected to. When I tried using the Web Inspector for Safari on Iphone, I see that there are no same-site cookies, so it seems that this is not the problem.
I see the document "en" in the list of resources on the Web Inspector when on the login page. It is type "document" and shows that inside of it is the following:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Sign In with Auth0</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
.bgimg {
background-image: url('pic.jpg');
}
#myVideo {
position: fixed;
right: 0;
bottom: 0;
min-width: 100%;
min-height: 100%;
z-index: 1;
}
.auth0-lock.auth0-lock.auth0-lock-opened .auth0-lock-overlay {
opacity: 0.0 !important;
-webkit-transition: opacity 0.3s ease-in 0s;
transition: opacity 0.3s ease-in 0s;
}
.video{
position:absolute;
z-index:-5 !important;
}
.overlay{
background:#333;
color: white;
position:fixed;
top: 50px;
left: 0;
width: 400px;
height: 100px;
z-index:1000;
visibility:hidden;
/*
* if you want to make it none clickable but make the
* clicks go to the video
*/
pointer-events: none;
}
</style>
</head>
<body background="https://www.nbn.org.il/wp-content/uploads/2019/04/auth0_bkg.jpg">
<div class="login-container"></div>
<!--
<div class ="video"> <video autoplay muted loop id="myVideo">
<source src="https://www.nbn.org.il/source_files/login/nbnommunitywaiting.mp4" type="video/mp4">
Your browser does not support HTML5 video.
</video>
</div>
-->
<!--[if IE 8]>
<script src="//cdnjs.cloudflare.com/ajax/libs/ie8/0.2.5/ie8.js"></script>
<![endif]-->
<!--[if lte IE 9]>
<script src="https://cdn.auth0.com/js/base64.js"></script>
<script src="https://cdn.auth0.com/js/es5-shim.min.js"></script>
<![endif]-->
<script src="https://cdn.auth0.com/js/lock/11.3/lock.min.js"></script>
<script>
// Decode utf8 characters properly
var config = JSON.parse(decodeURIComponent(escape(window.atob('long_token'))));
config.extraParams = config.extraParams || {};
var connection = config.connection;
var prompt = config.prompt;
var languageDictionary;
var language;
if (config.dict && config.dict.signin && config.dict.signin.title) {
languageDictionary = { title: config.dict.signin.title };
} else if (typeof config.dict === 'string') {
language = config.dict;
}
var loginHint = config.extraParams.login_hint;
var lock = new Auth0LockPasswordless(config.clientID, config.auth0Domain, {
auth: {
redirectUrl: config.callbackURL,
responseType: (config.internalOptions || {}).response_type ||
(config.callbackOnLocationHash ? 'token' : 'code'),
params: config.internalOptions
},
/* additional config needed to use custom domains
configurationBaseUrl: config.clientConfigurationBaseUrl,
overrides: {
__tenant: config.auth0Tenant,
__token_issuer: config.auth0Domain
}, */
assetsUrl: config.assetsUrl,
allowedConnections: connection ? [connection] : null,
rememberLastLogin: !prompt,
language: language,
languageDictionary: languageDictionary,
prompt: 'consent',
theme: {
logo:'pic.png',
primaryColor: "#fbaa40",
},
closable: false,
// uncomment if you want small buttons for social providers
// socialButtonStyle: 'small'
});
lock.show();
</script>
<div class="overlay">i'm a cool overlayed html block</div>
</body>
</html>
and then when I log in and go to the next page, under the Elements, section I get just a blank html body called about:blank.
Here is another image from the Web Inspector of the login page before getting to the first page of my app which may or may not be helpful:
Any insight into what might be wrong would be much appreciated!
My original thoughts about the issue posted above were completely not the problem.
At some point I realized this:
If I required login through auth0 and passed a context in the view the application downloaded html instead of rendering on iOS devices. If I didn’t require login, passing a context was no problem. If I didn’t pass in a context, requiring login through auth0 was no problem.
Even passing an empty context caused a problem.
Eventually I figured out that the problem was this:
In the Auth0 Django SDK that I was following, it shows that you should pass in what I thought was an extra dictionary parameter for auth0:
{'auth0User': auth0user,'userdata': json.dumps(userdata, indent=4) }
Since I didn't consider the other parameter to be a context, I also added my own context in the return statement of the view like this:
return render(request, 'mypage.html', context, { 'auth0User': auth0user, 'userdata': json.dumps(userdata, indent=4)} )
So for some reason this worked on pcs and androids to pass in two contexts, but iOS didn't like it.
Once I combined the two contexts
return render(request, 'mypage.html', {'contextvar1': 'data', 'contextvar2': 'moredata', 'auth0User': auth0user, 'userdata': json.dumps(userdata, indent=4)} )
everything works.
If anyone has understanding as to why this worked on other operating systems besides Apple and why this caused iOS devices to download the html, I would love to understand.
I can't seem to get the canvas+ webview engine within Cocoon to use a PIXI.WebGLRenderer no matter what I try. Here is a simple example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
<script type="text/javascript" src="http://pixijs.download/release/pixi.js"></script>
<title>Simple Test</title>
</head>
<body style="background-color: #99ff99; margin: 0; padding: 0;" onload="runTest()">
<script>
function runTest() {
var renderer = PIXI.autoDetectRenderer( 500, 200 );
document.body.appendChild( renderer.view );
var stageContainer = new PIXI.Container();
var text = new PIXI.Text( 'renderer.type='+renderer.type, { fontFamily: "Arial", fontSize: '12px', fill: "white" } );
text.x = text.y = 50;
stageContainer.addChild( text );
renderer.render( stageContainer );
}
</script>
</body>
</html>
With the Cocoon Launcher app I've tested this on:
iPhone5 (iOS 10.2)
iPad2 (iOS 9.3.5)
HP 10 Plus (android 4.4.2)
With webview+, all display "renderer=1" (which means PIXI.WebGLRenderer)
But with canvas+, all display "renderer=2" (which means PIXI.CanvasRenderer)
Is it possible to get canvas+ mode to work with WebGL in Pixi.js?
I have also tried forcing the renderer to be WebGL by replacing the autoDetectRenderer line with:
var renderer = new PIXI.WebGLRenderer( 500, 200 );
In this case my Cocoon Launcher on iphone and ipad crashes!
On the android, it just shows a black screen.
I have also tried with Pixi.js v3.0.1 with the same results.
So, is it possible to use WebGL with Cocoon's Canvas+ mode with Pixi.js?
Any help much appreciated, thanks
I managed to get it working, but not with the latest Pixi.js (v4.2.3), only with an old version - v3.0.10.
The key was to add cordova.js and use the document.addEventListener( "deviceready", runTest, false );
Here is some updated code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<script type="text/javascript" src="cordova.js"></script>
<script src="js/pixi-3.0.10.js"></script>
<title>Simple Test</title>
</head>
<body style="background-color: #99ff99; margin: 0; padding: 0;">
<script>
function runTest() {
var renderer = new PIXI.WebGLRenderer( 300, 100 );
document.body.appendChild( renderer.view );
var stageContainer = new PIXI.Container();
var text = new PIXI.Text( 'rrr.type='+renderer.type, { fontFamily: "Arial", fontSize: '12px', fill: "white" } );
text.x = text.y = 50;
stageContainer.addChild( text );
renderer.render( stageContainer );
}
document.addEventListener( "deviceready", runTest, false );
</script>
</body>
</html>
This is only a partial answer because it only works with an outdated version of Pixi.js
As for me, I'm abandoning canvas+ for now because what I got working showed poor and erratic results compared with the webview+ mode. Also, as an aside, according to various performance tests I've made, it seems that the old Pixi.js v3.0.10 is dramatically out performing v4.2.3 as well.. so looks like I'll be sticking with that also.
I use Phaser to create a game but I found an problem for using it in for example facebook. When an redirect is done within an iframe the canvas is not responding after a click.
Example:
I have a IFrame and within the iframe I redirect to the game.html. When I click in the game.html everything freezes.
Everything works fine when using a computer (any browser), windows phone or android, but with an iphone or ipad it won't work.
Below are the example files to replay the problem...
index.html:
<html>
<body>
<iframe src="click.html" height="900" width="800"/>
</body>
</html>
click.html
<!DOCTYPE html>
<html>
<body>
CLICK HERE
</body>
</html>
Game.html
<!DOCTYPE html>
<html>
<head>
<style>
body{overflow:hidden;}
#game_div {
width: 760px;
height: 1100px;
margin: auto;
}
</style>
<script type="text/javascript" src="./Game/phaser.min.js"></script>
<script type="text/javascript" src="./Game/main.js"></script>
</head>
<body>
<div id="game_div"> </div>
</body>
</html>
main.js
var game = new Phaser.Game(760, 1100, Phaser.AUTO, 'game_div');
var overlay, countdownText;
var counter = 0;
var main_state = {
preload: function() {
},
create: function () {
//game overlay
overlay = game.add.graphics(0, 0);
overlay.beginFill(0x00A54F, 0.8);
overlay.drawRect(0, 0, game.width, game.height);
countdownText = game.add.text((game.width / 2), (game.height / 2), counter, { font: "65px Arial", fill: "#ffffff", align: "center" });
countdownText.anchor.set(0.5,0.5);
},
update: function() {
countdownText.setText(counter++);
}
}
game.state.add('main', main_state);
game.state.start('main');
TNX
Found the solution, with thanks to Rich Davey.
When I add the following code it works:
game.stage.disableVisibilityChange = true;
Can anyone tell my why the contents of my InfoWindows are spilling out? When I view the InfoWindows in the Fusion Table UI, they all pop up and resize themselves correctly:
https://www.google.com/fusiontables/embedviz?q=select+col7+from+15wosKAeHC0gcpU_N6UPbxPL09RrEBKlQNEaCmnU&viz=MAP&h=false&lat=34.199813229302734&lng=-111.2955847411987&t=1&z=8&l=col7&y=2&tmplt=2
But when I try to use the html code to create a webpage, some (not all) of the InfoWindow contents spill out:
<!DOCTYPE html>
<html>
<head>
<title>CaveCreek - Google Fusion Tables</title>
<style type="text/css">
html, body, #googft-mapCanvas {
height: 300px;
margin: 0;
padding: 0;
width: 500px;
}
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
google.maps.visualRefresh = true;
var map = new google.maps.Map(document.getElementById('googft-mapCanvas'), {
center: new google.maps.LatLng(34.199813229302734, -111.2955847411987),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(document.getElementById('googft-legend'));
layer = new google.maps.FusionTablesLayer({
map: map,
heatmap: { enabled: false },
query: {
select: "col7",
from: "15wosKAeHC0gcpU_N6UPbxPL09RrEBKlQNEaCmnU",
where: ""
},
options: {
styleId: 2,
templateId: 2
}
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="googft-mapCanvas"></div>
</body>
</html>
My dynamic template InfoWindow code looks like:
{template .contents}
<div>
<div style="align: center">
<b>{$data.value.Stream}</b>
</div>
{if $data.value.y2012}
2012-{/if}
{if $data.value.y2012a}
2012a-{/if}
...
</div>
{/template}
I have tried to add height and width to the div tag for the InfoWindow in the template but that still doesn't seem to work.
Remove the visualRefresh, that is causing the infoWindow size to be calculated incorrectly.
google.maps.visualRefresh = true;
Note that the Google Maps visual refresh is going to be the default soon, so removing it is not a long-term solution.
If you set the height of the div and add overflow-y: scroll you should get a scrollbar on your content; does that not happen? One of the examples in the dynamic template help is setting the height differently based on fields being there or not, so that might help.
I've implemented the audio record plugin on IOS using phonegap/cordova 1.7. IOS5.1
I'm using it over the standard media.startrecord() function as I want to change the bitrate to reduce the size of the file.
It works fine in the simulator.
On the real iphone it records successfully once but subsequently refuses.. it seems to work but on playback I'm getting an error 4 MediaError.MEDIA_ERR_NONE_SUPPORTED
I've created a new project with just a bare bones record and playback and I'm still getting the same issue.
Here's the test code - It's very basic - just a couple of links to record two separate files and play them back. The setTimeout is there to record just a couple of seconds of audio each time.
I've googled til I've worn out my fingerprints but haven't found a resolution.
Any insight you can give would be very gratefully received.
I'm wondering if I'm failing to close out the recording/playback properly? Or are files referenced differently on the sim v the iPhone?
Many thanks!
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
<meta charset="utf-8">
<link rel="stylesheet" media="only screen and (max-device-width: 480px)" href="iphone.css" type="text/css" />
<script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
<script type="text/javascript">
var fs,mediaRec=null, recInterval,recordSettings = {"FormatID": "kAudioFormatULaw","SampleRate": 16000.0,"NumberOfChannels": 1,"LinearPCMBitDepth": 16};
function recfile1() {recordAudio("test.wav");}
function recfile2() {recordAudio("test2.wav");}
function success(){console.log("ok");}
function recordAudio(fname) {
fs.root.getFile(fname, {create: true, exclusive: false}, function(entry){
mediaRec = new Media(entry.fullPath, success, function(){alert("failed");});
mediaRec.startRecordWithSettings(recordSettings);
recInterval = setTimeout(function() {
mediaRec.stopRecordWithSettings();
}, 2000);
}, function(){console.log("error");});
}
function playfile(fname) {
var my_media;
fs.root.getFile(fname, {create: false, exclusive: false},
function success(entry) {
my_media = new Media(entry.fullPath,function(){console.log("ok");},function(err){alert(err.code+" "+err.message);});
my_media.play();
},
function() {
console.log("not found file");
}
);
}
function onBodyLoad() {
document.addEventListener("deviceready", onDeviceReady, false);
}
function onDeviceReady(){
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem){fs=fileSystem;}, function(){console.log("failed");});
Media.prototype.startRecordWithSettings = function(options) {
Cordova.exec(null, null, "AudioRecord","startAudioRecord", [this.id, this.src, options]);
};
Media.prototype.stopRecordWithSettings = function() {
Cordova.exec(null, null, "AudioRecord","stopAudioRecord", [this.id, this.src]);
};
}
</script>
</head>
<body onload="onBodyLoad()">
<a onclick="playfile('test.wav');">play</a>
<a style='margin-top:100px;' onclick="recfile1();">record 1</a>
<a style='margin-top:100px;' onclick="recfile2();">record 2</a>
<a onclick="playfile('test2.wav');">play2</a>
</body>
</html>