WKWebView: window.innerWidth reporting wrong value - wkwebview

I'm hosting some 3rd-party content in a WKWebView, and their content doesn't resize correctly on orientation change. It does resize correctly in Safari though.
window.onresize = function() { console.log(window.innerWidth) }
This prints the same width every time (768), regardless of orientation, but when I evaluate window.innerWidth on the console after the rotation has completed, it prints the correct width.
Is this a known issue? Are there any workarounds?
UPDATE:
What I ended up doing is re-firing the resize event after a delay. It's ugly but it solves my use case.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: nil) { _ in
// Orientation change triggers the `resize` event, but `window.innerWidth` is
// wrong *during that time*. By calling resize a second time, we let content resize itself
// according to the correct dimensions.
// There's more! Sometimes the dimensions are still wrong after the rotation has completed,
// so we have to wait. The minimum reliable delay varies widely across devices.
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(750)) {
self.webView?.evaluateJavaScript("window.dispatchEvent(new Event('resize'));", completionHandler: nil)
}
}
}

Yeah I had the same issue. You should use document.body.offsetWidth instead of window.innerWidth, because window.innerWidth can take additional 500ms until it updates itself after the window resize event. This is true at least for WKWebView in iOS.

Related

Cant quite nail scaling the game on mobile. Its not showing all of the game but only part of the game

this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
this.scale.pageAlignHorizontally = true;
this.scale.pageAlignVertically = true;
this.scale.refresh();
Its a tiny game code which I have uploaded on http://scaleissue.site44.com/ so pls use browser dev tools to have a look.
As you will notice when u go to dev tools and choose mobile emulation (iphone 4, 5, 6 - landscape orientation only), the screen only shows the top-left corner of the actual game with scroll bars when in fact it should have scaled the game such that all of the game was being shown on the screen since I used
this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
(you might have to reload page to notice this issue when in dev tools). I tried the game on real mobile and still the same issue.
All relevant scaling code is under boot.js
Please help as I cant quite nail the issue having tried several scaling modes.
You can try to resize the canvas:
var width=window.innerWidth,
height=window.innerHeight,
dpr=window.devicePixelRatio || 1;
var game = window.game[NAMESPACE].game = new Phaser.Game(1024, 480, Phaser.AUTO, 'gameDiv');
var mainState = {
preload : function () {
game.scale.scaleMode = Phaser.ScaleManager.RESIZE ;
//game.canvas.style.width=width+'px';
game.canvas.style.height=height+'px';
},
create : function () {
this.stage.backgroundColor = '#0000db';
},
update : function () {}
};
//initialising Phaser
game.state.add('main', mainState);
game.state.start('main');
The rationale behind this code: The canvas.width value (which is a number) sets the size of the game, while the canvas.style.width value (which is a string) sets the size of the canvas element. To avoid deformations you should maintain the same ratio (in your case, the ratio is 1024/480) in both values. If you set the ScaleManager as RESIZE then this ratio is maintained automatically, that's the reason why I'm only setting the height.

cocos2d js - Touch on overlay sprites that move up together

I used latest version cocos2d-js to create my game. On the game screen, I added multiple sprites overlay in a row, like this
Overlay sprites
I added an event listener to move up a sprite on y-axis when it was clicked. However, when I clicked on the point that any two sprites contain, the two sprites moved up together.
This is my event listener code
var listener = cc.EventListener.create({
event: cc.EventListener.TOUCH_ONE_BY_ONE,
swallowTouches: true,
onTouchBegan: function (touch, event) {
var target = event.getCurrentTarget();
var location = target.convertToNodeSpace(touch.getLocation());
var targetSize = target.getContentSize();
var targetRectangle = cc.rect(0, 0, targetSize.width, targetSize.height);
if (cc.rectContainsPoint(targetRectangle, location)){
target.setPositionY(50);
}
}
});
How can I prevent to move them up together and move only one sprite?
Thanks.
onTouchBegan must returns boolean value as result, if returns true it's means touch was handled and event cycle will be stopped. Try to return true, if rect contains point.
Hope this helps. And sorry for my English.

How to implement auto-hide navigation?

I'm not sure where to post this question, so please excuse me if I am violating any policies.
To clarify my question, I want to achieve the same navigation bar as Teehan + Lax's.
Here is their website: http://www.teehanlax.com/tools/
If you notice, the navigation auto hides itself when you scroll down, however when you scroll up it would show it self again.
So my question is, how did they achieve this? Is it through only CSS or do I need JavaScript to do this? Whatever way it is, can someone also point towards the right direction on how I can find the information to implement this?
Thank you
It's not possible to change position from fixed to absolute in pure CSS like you want, so I used some javascript to do so. Demo
function followTo(elem, pos) {
var element = document.getElementById(elem);
window.onscroll = function(e){
var disFromTop = document.all? iebody.scrollTop : pageYOffset;
if (disFromTop > pos) {
element.style.position = 'absolute';
element.style.top = pos + 'px';
} else {
element.style.position = 'fixed';
element.style.top = 0;
}
};
};
followTo("nav", 100);
It even includes an IE fix pulled from this SO post to get the correct scroll position
Here is the jQuery version, taken from this SO post
EDIT
As pointed out by zanona, I did not include the feature where the navigation appears if you scroll up from a place further down in the page. As a result, I create a new technique that uses a setInterval
var last = 0, // The last read top value
delay = 150, // The delay for the setInterval
threshold = 30; // The max scroll distance before showing/hiding the nav
//I always set a variable to my setIntervals in case I want to stop them later on
var navMovement = setInterval(function() {
var nav = document.getElementById('nav'), // Gets nav object
pageVertOffset = document.all? iebody.scrollTop : pageYOffset;
// Happens if the difference in scroll is below the negative threshold
if(pageVertOffset - last < -threshold) {
nav.style.top = "0px"; // Put the nav at the top of the window
}
// Happens if the difference in scroll is above the threshold
else if(pageVertOffset - last > threshold){
nav.style.top = - nav.offsetHeight + "px"; // Hides the navigation
}
last = pageVertOffset; // Updates the previous scroll value
}, delay); // Runs every `delay` amount
Javascript version, or if you prefer, jQuery version
I thought I recreated the site pretty well (but it's better because mine has a kitten, haha)

Shrink window in GTK+ dynamically when content shrinks?

I have a window in a Vala application and an image inside it.
This image is changed sometimes by img.set_from_pixbuf(imgdata); and so it's size changes as well. It's embedded in a Gtk.Box.
box = new Gtk.Box(Orientation.VERTICAL,5);
...
box.pack_end(img,false,false);
So if there was a big image before and I replace it with a smaller one, the window remains ridiculously big and I have not found a method to dynamically shrink it to the space required.
I have tried with window.set_default_size(box.width_request,box.height_request) but it always returns -1.
So any ideas how to resize the window?
Thanks!
I have fought with this issue myself and while the accepted answer is correct, I though I could give a more "complete" answer, with working code.
Reproducing the problem
The following code (In C++, sorry) reproduces your issue:
#include <array>
#include <gtkmm.h>
class ResizableWindow : public Gtk::Window
{
public:
ResizableWindow()
: m_toggle{"Toggle"}
, m_currentImageIndex{0}
{
m_files[0] = "small.png";
m_files[1] = "large.png";
// Setup window layout:
m_layout.attach(*Gtk::manage(new Gtk::Image(m_files[m_currentImageIndex])), 0, 0, 1, 1);
m_layout.attach(m_toggle, 0, 1, 1, 1);
add(m_layout);
// Set up signal handlers:
m_toggle.signal_clicked().connect([this](){OnToggle();});
}
private:
void OnToggle()
{
// Switch image file:
if(m_currentImageIndex == 0)
{
m_currentImageIndex = 1;
}
else
{
m_currentImageIndex = 0;
}
// Load new image.
Gtk::Widget* child = m_layout.get_child_at(0, 0);
Gtk::Image* currentImage = dynamic_cast<Gtk::Image*>(child);
currentImage->set(m_files[m_currentImageIndex]);
}
Gtk::Grid m_layout;
Gtk::Button m_toggle;
std::array<std::string, 2> m_files;
size_t m_currentImageIndex;
};
int main (int argc, char* argv[])
{
auto app = Gtk::Application::create(argc, argv, "so.question.q8903140");
ResizableWindow w;
w.show_all();
return app->run(w);
}
The Toggle button changes the underlying images. Both are the same image, but with different sizes. Notice that, As you already mentionned, when toggling for the first time (small --> large), the window resizes appropriately. However, when toggling a second time (large --> small), the image is resized, but not the window, leaving extra space around the image:
Weird, I know...
Solution
To solve the issue, one needs to call the resize method. So the Toggle handler would become:
void OnToggle()
{
if(m_currentImageIndex == 0)
{
m_currentImageIndex = 1;
}
else
{
m_currentImageIndex = 0;
}
Gtk::Widget* child = m_layout.get_child_at(0, 0);
Gtk::Image* currentImage = dynamic_cast<Gtk::Image*>(child);
currentImage->set(m_files[m_currentImageIndex]);
// Resize window:
resize(1, 1);
}
Note that resize was called with dimensions 1x1 (smallest possible dimensions). Gtkmm will resize the window following geometry constraints automatically from there.
If I am not mistaken the automatic resizing of windows only only happens when elements are too large to be drawn. Additionally the set_default_size method only matters when first drawing the window and unless I am wrong is never used again. I would suggest using the resize method to set the window size. (link)
window.resize(box.width_request, box.height_request);
One thing you need to remember when using resize if you can't resize it smaller than the request_size if you run into that issue use the set_request_size method.

Titanium handling different resoutions

Simple question, which is the best way to make sure that the app works on different screen resolutions without looking crap? I can't use static values, then it want adjust according to the resolution. Right now i am using relative measurements (percentage of screen) but wonder if that's really the best way to handle it!?
Another/additional option we have been successful with is a small set of functions which use the screen density value to compute display sizes... this is of course only an approximation, as there are only the four densities, but we have found this to be very helpful.
//=============================================================================
// inch
//
// compute approximate number of pixels for the specified physical on-screen
// size based on the density reported by the OS
//=============================================================================
function inch(size)
{
// default to 160 dpi if unavailable
var height = size * 160.0;
try
{
// compute header height based on screen density ... target .25" height
height = size * Ti.Platform.displayCaps.dpi;
}
catch(e) { warn("Error accessing display caps for screen density calculation: " + e); }
return height;
}
So if you want something to be 3/4 inch high on the screen....
Ti.UI.createThing({ height: inch(.75)});
...or if you want to scale things by point size, one could make some constants...
const g_12pt = inch(12/72); //0.166667
const g_10pt = inch(10/72); //0.138889
const g_8pt = inch(8/72); //0.111111
const g_6pt = inch(6/72); //0.083333
...
...font:{fontSize: g_10pt, fontWeight:"bold"},...
We also created a couple of functions to get the screen height and width, so if we wanted a better layout on a tablet or something tiny we could better understand what we were dealing with.
//=============================================================================
// screenWidth - return screen width in inches
//=============================================================================
function screenWidth()
{
return Titanium.Platform.displayCaps.platformWidth / Titanium.Platform.displayCaps.dpi;
}
//=============================================================================
// screenHeight - return screen height in inches
//=============================================================================
function screenHeight()
{
return Titanium.Platform.displayCaps.platformHeight / Titanium.Platform.displayCaps.dpi;
}
You can go on and on from there... but this really helped us nail down how we laid out our screens on the different densities and platforms. The inch function has exception handling only because we use it early in the app, and sometimes the Ti.Platform is still undefined. By the time we are laying out our reports Ti.Platform is available and so the screen functions do not have the exception handling. If you need to query earlier you may need exception handling in those as well.
Hope this helps!
You'll want to modify your tiapp.xml - in the android section you add a manifest record like so:
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
<supports-screens android:anyDensity="false"/>
</manifest>
</android>
That will make the app default to the same style that the older versions of titanium used, where it basically scales them according to the device in use. More details on the change can be found here: http://developer.appcelerator.com/blog/2011/06/new-defaults-for-android-layouts-in-1-7.html
well, as far as I googled, the solution is :
1.detect the specific screen resolution:
// app/alloy.js
Alloy.Globals.isIos7Plus = (OS_IOS && parseInt(Ti.Platform.version.split(".")[0]) >= 7);
Alloy.Globals.iPhoneTall = (OS_IOS && Ti.Platform.osname == "iphone" && Ti.Platform.displayCaps.platformHeight == 568);
2.write query style tss:
// Query styles
"#info[if=Alloy.Globals.isIos7Plus]" : {
font : { textStyle : Ti.UI.TEXT_STYLE_FOOTNOTE }
},
"#title[if=Alloy.Globals.isIos7Plus]" : {
top: '25dp', // compensate for the status bar on iOS 7
font : { textStyle : Ti.UI.TEXT_STYLE_HEADLINE }
},
"#content[if=Alloy.Globals.isIos7Plus]" : {
font : { textStyle : Ti.UI.TEXT_STYLE_CAPTION1 }
},
"ScrollView[if=Alloy.Globals.iPhoneTall]" : {
height : '500dp'
}
refer to: http://docs.appcelerator.com/titanium/3.0/#!/guide/Alloy_Styles_and_Themes-section-35621526_AlloyStylesandThemes-Platform-SpecificStyles

Resources