Ionic-framework: [4.0.0-rc.2] ion-slides do not resize after screen orientation changes in iOS

Created on 18 Jan 2019  路  18Comments  路  Source: ionic-team/ionic-framework

Bug Report

Ionic version:
[x] 4.x

Current behavior:
[4.0.0-rc.2] ion-slides do not resize after screen orientation changes in iOS

Expected behavior:
ion-slides should resize when orientation changes in iOS

Steps to reproduce:

  1. Create a new ionic 4 app and add 3 or 4 slides. Or take ionic-conference-app and upgrade to 4.0.0-rc.2
  2. Build project for iOS
  3. Install on device or run in simulator and change orientation

Ionic info:

Ionic:

   ionic (Ionic CLI)             : 4.7.1 (C:\Users\gigoc\AppData\Roaming\npm\node_modules\ionic)
   Ionic Framework               : @ionic/angular 4.0.0-rc.1
   @angular-devkit/build-angular : 0.12.1
   @angular-devkit/schematics    : 7.1.4
   @angular/cli                  : 7.1.4
   @ionic/angular-toolkit        : 1.2.2

Cordova:

   cordova (Cordova CLI) : not installed
   Cordova Platforms     : not available
   Cordova Plugins       : not available

System:

   NodeJS : v8.11.3 (C:\Program Files\nodejs\node.exe)
   npm    : 6.5.0
   OS     : Windows 10
investigation core

Most helpful comment

My recipe:

Template:

<ion-slides #slides>
...
</ion-slides>

Class:

  ...
  @ViewChild('slides') slides;
  ...
  @HostListener('window:resize')
  onResize() {
    setTimeout(() => this.slides.update(), 100);
  }

I'm using setTimeout() because I'm using <ion-split-pane> and there can be one more resize of the <ion-content> after the browser window resize. And listen to ionSplitPaneVisible makes everything much more complicated. But if you are not using additional elements affecting component size, you can easily drop the setTimeout.

But still 鈥斅爄t'll be better if the component listens to it's size changes internally.

HTH

All 18 comments

Some examples:

first:
img_da9e9a367200-1

Rotated:
img_b4eb11c34078-1

Rotated back:
img_c56629e9e836-1

It should show a single slide every time.

If it helps it breaks in seemingly random ways, sometimes showing 2,5 on rotation and a single time it worked as expected so possibly a timing issue ?

+1

@paulstelzer Any news on this ?

Can confirm this is not working properly on simulator and real device. Rotating to landscape doesn't cause it to dynamically update the width on as it does on android so width is too short and shifted off center to left, also results in it being off center when returning to portrait as well.

My recipe:

Template:

<ion-slides #slides>
...
</ion-slides>

Class:

  ...
  @ViewChild('slides') slides;
  ...
  @HostListener('window:resize')
  onResize() {
    setTimeout(() => this.slides.update(), 100);
  }

I'm using setTimeout() because I'm using <ion-split-pane> and there can be one more resize of the <ion-content> after the browser window resize. And listen to ionSplitPaneVisible makes everything much more complicated. But if you are not using additional elements affecting component size, you can easily drop the setTimeout.

But still 鈥斅爄t'll be better if the component listens to it's size changes internally.

HTH

Any news on this ?

@leechy At least on ios i still need a timeout even without surrounding elements. Also and this will sound strange, it will still go "wrong" when rotating the device slowly...

The core issue is iOS will call the resize and orientationChange event before the viewport was resized (see this link for some details). ion-slides (in detail the external swiper library) is listen to this event and calculation the sizes of the slides right after this event will fired.
There are two solutions to solve this issue. One solution was already mentioned: call slides.update() after a specific timeout e.g.: 300ms.
Another solution is to disable some calculation in swiper by set the option slidesPerColumn to auto (<ion-slides options="{'slidesPerColumn':'auto'}">). But this solution will cause some other issues. So at the moment calling slides.update() maybe several times after a timeout is the simplest solution for this problem.
BTW: swiper also has an option to only recalc their dimensions by calling swiper.emit('resize'). But this api is not exposed by ion-slides.

This is still an issue in release 4.3.0

I created a directive off of @leechy's recipe to patch slides automatically and reduce boilerplate if anyone would find this useful.

import { Directive, ElementRef, HostListener } from '@angular/core';
import { Platform } from '@ionic/angular';

@Directive({
  // tslint:disable-next-line: directive-selector
  selector: 'ion-slides',
})
export class PatchSlidesDirective {
  public get slides() {
    return this.el && this.el.nativeElement;
  }

  constructor(
    public el: ElementRef<HTMLIonSlidesElement>,
    public platform: Platform,
  ) {}

  @HostListener('window:resize')
  public onResize() {
    if (this.platform.is('ios')) {
      setTimeout(() => {
        if (this.slides) {
          this.slides.update();
        }
      }, 100);
    }
  }
}

I've seen this bug on Android, too.

I found that putting a call to this.slider.update(); in ionViewWillEnter() fixed my problem.

I have this problem also with ionic 4.6. The workaround does not work on every time in my case. Sometimes the problem comes back and sometimes it can resolve by another orientation change. Strange and ugly

My solution...

npm i angular-resize-event

CSS:

ion-slide {
    width: 100% !important;
}

HTML:

<ion-slides #slider>
    <ion-slide (resized)="slider.update()">One</ion-slide>
    <ion-slide (resized)="slider.update()">Two</ion-slide>
    <ion-slide (resized)="slider.update()">Three</ion-slide>
</ion-slides>

For me works update to Ionic 4.9.1 and code from @adamduren and styles from @kyleabens

This issue is still a problem with Ionic 4.11.8

Please have a look in my linked post.

I'm trying desperately to figure out what could be wrong.
I think it has to do with how swiper.bundle.js does device platform detection.
This doesn't seem to be handed off to capacitor's device plugin, cordova's device.platform or @ionic/angular's platform for that matter, but has it's own window.navigator.platform and window.navigator.useragent script to check the correct device platform.

I'm guessing this is where some iOS devices aren't recognized and therefore the orientationchange event is not added to the swiper.

I could be wrong here. Let me know if I'm aiming in the right direction.

Ofcourse you could simply manually call slider.update() but that doesn't seem like a proper fix. More like a workaround.

EDIT:
Someone else also mentioned that the following css in globals.scss would also fix the issue as a workaround:

.swiper-slide-active {
width: 100% !important;
}

haven't tried this yet though.

@Simbaclaws thank you! Works on iOS! :)

Having same issue, this bug is very old, no fix so far?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

brandyscarney picture brandyscarney  路  3Comments

danbucholtz picture danbucholtz  路  3Comments

manucorporat picture manucorporat  路  3Comments

Nick-The-Uncharted picture Nick-The-Uncharted  路  3Comments

giammaleoni picture giammaleoni  路  3Comments