iOS 13 и Responsive Config

Столкнулся с проблемой работы responsiveConfig на iOS 13.x . Описываемые формулы не всегда работали правильно (путали landscape, portrait). Для начала создадим тестовое приложение, очень простое, всего лишь с двумя формулами.

Ext.define('MyApp.view.main.Main', {
    extend: 'Ext.Container',
    layout: 'fit',
    items: [{
            xtype: 'button',
            responsiveConfig: {
                'landscape': {
                    text: 'landscape'
                },
                'portrait': {
                    text: 'portrait'
                },
            },
        },
    ]
});

В зависимости от ориентации экрана, мы будем менять текст кнопки. Соберем наше приложение, и задеплоим например сюда. Теперь давайте откроем наше приложение в браузере на iPad’e. Тестировать будем на трех браузерах Chrome, FireFox, Safari.

Chrome 78.0.3904.84
Firefox 20.2 (16690)
Safari (13.0.3)

Как мы видим, адекватное поведение только в Safari. В остальных случаях мы видим баг. Для того что бы понять причину бага, следует посмотреть на текущий Ext.mixin.Responsive.context. И там будет видно что, height и width рассчитываются верно, а такие параметры landscape и portrait нет. Последние рассчитываются с помощью функции Ext.dom.Element.getOrientation.

// WIN - это window
// Я вижу что тут забыли что portrait ориентация это [0,180].includes(window.orientation)
// Все остальное landscape
getOrientation: function() {
    if (Ext.supports.OrientationChange) {
        return (WIN.orientation == 0) ? 'portrait' : 'landscape';
    }
    return (WIN.innerHeight > WIN.innerWidth) ? 'portrait' : 'landscape';
}

После того как мы добавили поддержку 180 градусов, согласно этому документу Мой iPad перестал показывать landscape в перевернутом виде. Это решило часть проблемы, но не полностью. После долгой и нудной отладки процедуры Ext.mixin.Responsive.updateContext (которая вызывается при resize событии window) было замечено что window.orientation остается старым на момент исполнения процедуры (и соответственно мы не правильно рассчитываем landscape, portrait).

В качестве решения, я предлагаю вызывать обновление контекста (для iOS 13.x) не при событии resize, а при orientationchange (да, это будет медленнее, но там нет этого race condition)

Прикладываю два override которые помогли мне решить проблему

/**
 * @class Base.overrides.dom.Element
 * @author Dmitry Kazarin <dikazarin@gmail.com>
 * 
 * Альтернативная логика определения ориентации.
 * Исправляет баг с определением ориентации.
 * 
 **/

Ext.define('Base.overrides.dom.Element', {
	override: 'Ext.dom.Element',

    statics: {
        getOrientation: function() {
            if (Ext.supports.OrientationChange) {
                return (window.orientation == 0 || window.orientation == 180) ? 'portrait' : 'landscape';
            }
            return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
        },
    }

});
Ext.define('Base.overrides.mixin.Responsive', {
    override: 'Ext.mixin.Responsive',
    privates: {
        statics: {
            activate: function() {
                const Responsive = Ext.mixin.Responsive;
                Responsive.active = true;
                Responsive.updateContext();
                if (Ext.is.iOS) {
                    Ext.Viewport.on('orientationchange', Responsive.onResize, Responsive);
                } else {
                    Ext.on('resize', Responsive.onResize, Responsive);
                }
            },
            deactivate: function() {
                const Responsive = Ext.mixin.Responsive;
                Responsive.active = false;
                if (Ext.is.iOS) {
                    Ext.Viewport.un('orientationchange', Responsive.onResize, Responsive);
                } else {
                    Ext.un('resize', Responsive.onResize, Responsive);
                }
                
            },
        }
    },
    function() {
        Ext.Object.each(Ext.ClassManager.classes, function(name, cls) {
            if (cls.prototype && cls.prototype.mixins && cls.prototype.mixins.hasOwnProperty('responsive')) {
                cls.prototype.activate = this.prototype.activate;
                cls.prototype.deactivate = this.prototype.deactivate;
            }
        }, this);
    }
});

Можно было конечно поступить проще, и убрать использование Ext.supports.OrientationChange и window.orientation из Ext.dom.Element.getOrientation. Но там видимо ещё что то есть, не во всех случаях работает правильно (а значит есть какой то race condition с обновлением window.orientation)

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *