HammerJS pan is not working on iOS (under Ionic) - ios

I am programming an app in Ionic and using hammer.js for the pan gesture (for the moment, only this one). In the browser (chrome and safari) this is working fine, also on Android device, but I just discovered that pan gesture is not working on iOS (neither real device nor emulator). After a long while research, I don't find the way to make it work. Neither pan nor panend work (click does).
Note: The events are (should be) launched from a custom component, not a page itself.
I have seen other with similar problems, but no solution that works for me. Here is finally stated that hammerjs it not so compatible with iOS, but here says it it. This guy seems to have a similar problem, but found no solution.
I have also try adding to my app.module.ts the following code and the provider {provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig}. No success.
import * as Hammer from 'hammerjs';
import {HammerGestureConfig, HAMMER_GESTURE_CONFIG} from '#angular/platform-browser';
export class MyHammerConfig extends HammerGestureConfig {
overrides = <any> {
'pan': {
enable: true,
direction: Hammer.DIRECTION_HORIZONTAL
}
};
}
Hier is (an extract of) my code:
export class SeekbarQuantizedComponent implements OnInit, OnChanges {
private stepsPositions: number[]; // Will have the position of the different steps (in %)
private progress = 0; // The progress of the progressbar in %
[...]
onClick($event: MouseEvent) {
const clickX = $event.offsetX * 100 / this.background.nativeElement.offsetWidth;
this.setProgress(clickX);
this.valueSelected.emit(this.selectedPosition);
}
onPan($event) {
this.setProgress(100 * ($event.changedPointers[0].offsetX / this.background.nativeElement.offsetWidth));
}
onPanEnd($event) {
this.setProgress(100 * ($event.changedPointers[0].offsetX / this.background.nativeElement.offsetWidth));
this.valueSelected.emit(this.selectedPosition);
}
[...]
}
The html:
<div #background class="progress-outer"
(click)="onClick($event)"
(pan)="onPan($event)"
(panend)="onPanEnd($event)">
<div class="progress-inner"
[style.width]="progress + '%'">
{{text}}
</div>
</div>

Related

Disable window offset when keyboard appears for ios app

Disable window offset when keyboard appears for ios app
I would like to disable the window offset when the keyboard appears for the ios app. For an android app, this is done via AndroidManifest.xml:
<activity ... android:windowSoftInputMode="adjustResize">
When the keyboard appears, the components do not move from their places.
Is it possible to get this behavior for ios?
Found the solution here in the comments:
iOS Make window-scrolling optional for Items
https://bugreports.qt.io/browse/QTBUG-80790
(this workaround works well for me)
========================
Adrian Eddy added a comment - 11 Apr '20 01:44 - edited
I found a workaround that works with QML . The idea is to install an event filter on QQuickItem and listen for a QEvent::InputMethodQuery with Qt::InputMethodQuery::ImCursorRectangle. Then we set its value to empty QRectF and Qt will no longer scroll the view to show that text field.
in C++ prepare a class and expose it to QML:
class Api : public QObject {
Q_OBJECT
....
public:
Q_INVOKABLE void setupImEventFilter(QQuickItem *item) {
static thread_local ImFixer imf;
item->installEventFilter(&imf);
}
}
// somewhere in main():
view.rootContext()->setContextProperty("api", new Api());
We'll need the actual event filter too:
class ImFixer :
public QObject {
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event) override {
if (event->type() == QEvent::InputMethodQuery) {
QInputMethodQueryEvent *imEvt = static_cast<QInputMethodQueryEvent *>(event);
if (imEvt->queries() == Qt::InputMethodQuery::ImCursorRectangle) {
imEvt->setValue(Qt::InputMethodQuery::ImCursorRectangle, QRectF());
return true;
}
}
return QObject::eventFilter(obj, event);
}
};
Finally in QML add:
TextField {
id: tf;
...
Component.onCompleted: api.setupImEventFilter(tf);
}

How to show a video from gallery in Ionic iOS

I am using html5's video tag to show a video I select from the gallery. I'm getting an issue where the video doesn't load even though I've provided a source.
This is an Ionic/Angular project with Capacitor as the bridge, but still using Cordova plugins. Here is a prototype of my code:
my-page.page.ts
import { Camera, CameraOptions } from '#ionic-native/camera/ngx';
import { Capacitor } from '#capacitor/core';
#Component({...})
export class MyPage {
// ... some code which gets the a reference to the video element from the template
// this function is called when the user clicks a button to choose video from gallery
onPickVideo() {
const cameraOptions: CameraOptions = {
destinationType: this.camera.DestinationType.NATIVE_URI,
sourceType: this.camera.PictureSourceType.SAVEDPHOTOALBUM,
mediaType: this.camera.MediaType.VIDEO,
};
this.camera.getPicture(cameraOptions).then(videoUri => {
console.log(videoUri);
this.videoSrc = Capacitor.convertFileSrc(videoUri);
console.log(this.videoSrc);
this.videoEl.load();
this.videoEl.currentTime = 0.1;
});
}
}
my-page.page.html
<video playsinline #video>
<source [src]=".videoSrc" type="video/mp4" />
Your browser does not support the video tag.
</video>
The output of my-page.page.ts is:
file:///private/var/mobile/Containers/Data/Application/7D85727C-CE9A-4816-BC42-C97F03AFDEB4/tmp/F6DCE819-ED4A-41E4-BAFB-814500F2FB27.MOV
capacitor://localhost/_capacitor_file_/private/var/mobile/Containers/Data/Application/7D85727C-CE9A-4816-BC42-C97F03AFDEB4/tmp/F6DCE819-ED4A-41E4-BAFB-814500F2FB27.MOV
This works on desktop and on Android. It's not working on iPhone 6 with iOS 12. Untested on other iOS versions.
Some things I've tried:
Added NSCameraUsageDescription, NSPhotoLibraryUsageDescription, NSPhotoLibraryAddUsageDescription, NSMicrophoneUsageDescription
Used [src]= in the video tag, and removed the source tag. Or omitting the 'video/mp4' type
Running in live reload mode vs just building.
Chopping 'file:///' off the start of videoUri before passing it to convertFileSrc(). Or doing the former and directly setting it to videoSrc without using convertFileSrc() at all.
Solved by "sanitizing" the URL. I'm yet to learn what that really means. Here is the code in case anyone needs it
import { Camera, CameraOptions } from '#ionic-native/camera/ngx';
import { Capacitor } from '#capacitor/core';
import { DomSanitizer, SafeUrl } from '#angular/platform-browser';
#Component({...})
export class MyPage {
// ... some code which gets the a reference to the video element from the template
safeUrl: SafeUrl;
constructor(private sanitizer: DomSanitizer) {}
// this function is called when the user clicks a button to choose video from gallery
onPickVideo() {
const cameraOptions: CameraOptions = {
destinationType: this.camera.DestinationType.NATIVE_URI,
sourceType: this.camera.PictureSourceType.SAVEDPHOTOALBUM,
mediaType: this.camera.MediaType.VIDEO,
};
this.camera.getPicture(cameraOptions).then(videoUri => {
this.safeUrl = this.sanitizer.bypassSecurityTrustUrl(
Capacitor.convertFileSrc(videoUri)
);
this.videoEl.load();
this.videoEl.currentTime = 0.1;
});
}
}
Then make sure to be using safeUrl in the template [src]="safeUrl".

Electron: [Violation] 'click' handler took 574ms

I'm building a SPA, and everything works fine in Chrome browser. I try to run the same code in Electron, and I have the following error (when clicking on a button):
[Violation] 'click' handler took 574ms
Moreover, right after this error, all IPC calls between webview and guest page (via ipcRenderer.sendToHost) are blocked, i.e. not arriving to the webview.
Googling a bit taught me that the limit for blocking UI is 50ms. Is there a way I can increase this limit?
Edit: Here's the code which handles the click. It's a tabbar with one tab. Code is reactjs.
import { Link } from 'react-router';
import { Tab as MUITab } from 'material-ui/Tabs';
class TabBar extends Component {
render() {
const view = {
to: '/accounts',
id: 'accounts',
icon: 'home',
label: 'ACCOUNTS'
};
return (
<Link to={ view.to }> // <--- <a> tag in DOM, receives the click
<Tab view={ view } />
</Link>
);
}
class Tab extends Component {
static propTypes = {
view: PropTypes.object.isRequired
};
render () {
const { view } = this.props;
return (
<MUITab
icon={ view.icon }
label={
this.renderLabel(view.id)
}
/>
);
}
renderLabel (id, bubble) {
return (
<div className={ styles.label }>
<FormattedMessage
id={ `settings.views.${id}.label` }
/>
{ bubble }
</div>
);
}
}
Googling a bit taught me that the limit for blocking UI is 50ms. Is there a way I can increase this limit?
Probably not the answer you want, but I'd rather suggest refactor code not blocking. 50ms is noticeable time for users, and if your code blocks average 500ms, it'll make noticeable UI lags to user. Electron's IPC have asynchronous mechanism - try to avoid sync but use async one instead to make UI non-blocking.

Adobe AIR, IOS, and Multiple SWFs - Returning to a previously loaded SWF

I am developing an IOS app using Adobe AIR, Flash and ActionScript 3 and I have divided sections up into separate SWF files. I have successfully gotten the app to load external SWFs and execute their ActionScript using the following code:
var proloader:ProLoader = new ProLoader();
proloader.load(new URLRequest("file.swf"), new LoaderContext(false, ApplicationDomain.currentDomain, null));
addChild(proloader);
Now, I would like to add a reset button that allows the user to return to the start of the first SWF from any other SWF file to restart the app. Unfortunately, it seems that whenever I try to load a SWF that has previously been loaded, nothing happens. I have read that unloading and reloading SWFs is not permitted on IOS and the fact that you are limited to one ApplicationDomain on IOS makes things difficult. However, I am still thinking there must be some workaround. I'd be okay with not ever unloading the external SWFs if that is the only way, but I still can't figure out a way to return to a SWF that was previously loaded.
Does anyone know of a way to return to a SWF that was previously loaded in IOS with Adobe Air?
You could have a static class that functions as a cache.
The cache might look something like this that I made for a menu system:
package com.rs2014.managers{
import flash.display.MovieClip;
import flash.utils.Dictionary;
import flash.events.Event;
import flash.events.EventDispatcher;
import com.events.MenuManagerEvent;
import com.menus.MenuBase;
import com.components.MenuLoader;
import com.MenuIdents;
public class MenuManager extends EventDispatcher
{
private static var instance:MenuManager
private static var allowInstantiation:Boolean = true;
public static function getInstance():MenuManager
{
if(!instance)
{
instance = new MenuManager();
allowInstantiation = false;
}
return instance
}
private const MAX_MENUS:uint = 8;
private var menuCache:Dictionary = new Dictionary()
public function MenuManager()
{
if(!allowInstantiation)
{
throw(new Error("please use the getInstance() method to obtain a handle to this singleton"));
}
}
public function getMenu(menu:String):void
{
if(menuCache[menu])
{
//This pulls from the cache if it's already been loaded before
dispatchMenu(null, menuCache[menu]);
}
else
{
//MenuLoader is a simple wrapper for Loader
var newLoader:MenuLoader = new MenuLoader();
menuCache[menu] = newLoader;
newLoader.addEventListener(Event.COMPLETE, dispatchMenu);
newLoader.source = menu;
//trace("setting up loader with source = "+menu);
}
}
private function dispatchMenu(e:Event = null, menu:MenuBase = null):void
{
if(e)
{
e.target.removeEventListener(Event.COMPLETE, dispatchMenu);
//trace(e.target.content);
//trace(e.target.content as MenuBase);
menuCache[e.target.source] = menu = e.target.content as MenuBase;
//trace(menu);
}
if(!menu)
{
throw(new Error("MenuManager Error: dispatchMenu called without menu to dispatch"));
}
this.dispatchEvent(new MenuManagerEvent(MenuManagerEvent.MENU_READY, menu))
}
}
}
I was able to solve the problem of reloading SWF's w/o ABC (no code!), I explain it all on my blog (http://www.eqsim.com/blog/?p=400) but also simply give the code on StackOverflow: Load and reload external SWF in AIR for iOS wf-in-air-for-ios/22046009#22046009
Now Adobe has a solution for separating code from the SWF, and loading the SWF externally. However, we addressed it prior to AIR 3.5 by making a class for the code only, then loading the SWF and in the code class, setting a link to the loaded SWF. When we needed to update to 4.0, the SWF reloading, even pure SWF assets, was not working in all situations, so we plugged away to find the solution I reference above.

dart onTouchMove compiled to JS delay

I wonder what's wrong. In emulated chrome touch, it worked perfectly. In mobile compiled to JavaScript chrome browser, does not work perfectly.
*Tested on Android 4.1 - Chrome 30 - dual core
Dart Editor version 0.8.1_r28355
Dart SDK version 0.8.1.2_r28355*
import 'dart:html';
DivElement divContainer;
void main() {
divContainer = new DivElement()
..style.backgroundColor = '#6a00d7'
..style.width = '600px'
..style.height = '600px';
document.body.nodes.add(divContainer);
window.onTouchMove.listen((event) => mov(event));
}
mov(evento){
divContainer.innerHtml = evento.touches[0].client.x.toString();
}
Add inside the method call:
event.preventDefault();
Prevents shortcuts "touch" interfere (chrome mobile).
Method code:
mov(evento){
evento.preventDefault();
divContainer.innerHtml = evento.touches[0].client.x.toString();
}

Resources