Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fullscreen mode on IOS breaks layout when video ends #1319

Closed
jonathangreco opened this issue Nov 9, 2018 · 59 comments
Closed

fullscreen mode on IOS breaks layout when video ends #1319

jonathangreco opened this issue Nov 9, 2018 · 59 comments

Comments

@jonathangreco
Copy link

jonathangreco commented Nov 9, 2018

Current behavior

On IOS with the native players buttons I read a video and go to fullscreen.
When the video finishes, i go back to my app and then I switch from portrait to landscape (or vice versa)

Reproduction steps

  • Take any video.
  • Play it on a player with controls set to true on IOS to have the native controls up.
  • Switch to full screen.
  • then when the video finishes (you can stop it manually) change layout mode (portrait / landscape) of your device

Then your app not taking the whole screen.

Expected behavior

The app should take the whole screen.

Platform

Which player are you experiencing the problem on:

  • iOS
@jonathangreco jonathangreco changed the title App on layout not take the whole space of the screen after video played in fullscreen mode on IOS fullscreen mode on IOS crash layout when video ends Nov 19, 2018
@jonathangreco
Copy link
Author

@cobarx ping :)

Hi.

This bug has been tested on Ipad air 2 and Iphone 8.
I can give a quick video that shows the issue.

here's multiple screens that tries to explain how to reproduce it (taken from Iphone 8 IOS 12.1):

Initial state of the screen
capture d ecran 2018-11-20 a 11 12 17

on click we show the video, and native controls set to true we go to fullscreen :
capture d ecran 2018-11-20 a 11 13 18

Then 2 bugs are happening here (on both Ipad Air & Iphone 8) when we go out of fullscreen :
capture d ecran 2018-11-20 a 11 13 36

You can see now that the layout is broken, means that it's now take half of the screen. The upper side is scrollable tho.
If we not go to fullscreen this problem never happen.

And if we stay on fullscreen until the video ends, on Ipad Air only screen freeze. We never go back to the app and need to restart it to take control again.

Questions

Is someone managed to reproduce this bug ?
Is possible to deactivate fullscreen option in controls, but still have native controls available on IOS ?

I tried to reproduce it with a snack.expo.io but it seems that react-native-video does not work with snack : https://snack.expo.io/@jonathan01/iphone-fullscreen

@jonathangreco
Copy link
Author

@cobarx ping

What do you think about this issue ? Did you ever seen it ?

@cristiangu
Copy link

Still happens. Tested on iPad. After a video goes full screen and back, the view is not resized anymore when the tablet orientation change.

@jonathangreco
Copy link
Author

@cristiangu So its reproducable, that's a good thing. Thanks for your feedback on this.

@jonathangreco
Copy link
Author

@cristiangu Do you have a little basic white project that we can launch that reproduce this error ? With this it will be easier for maintainers like @cobarx to fix it.

@Iamivan1996
Copy link

same here, layout is broken after the video goes full screen and back on IOS device

@Iamivan1996
Copy link

Iamivan1996 commented Jan 2, 2019

I found that if i pressing the full screen button in the control bar, although the layout is broken, the full screen is take the whole screen that what i need.

If i run this code this.player.presentFullscreenPlayer(); the layout are not broken again and return to previous, but the video is not take the whole screen, it just change the screen to black and place the video into top-center , i tried to use resizeMode with "contain, cover, stretch" , the fullscreen style is still not same as the full screen button.

@jonathangreco
Copy link
Author

@cobarx Hi, is this issue followed by anyone ? Since 2 month now and no maintainers take this issue.

@TylerWardle
Copy link

I'm having similar issues. When controls={true} fullscreen layout is broken. Everything is fine however when the controls prop is unspecified.

@jonathangreco
Copy link
Author

jonathangreco commented Jan 15, 2019

@cobarx @7nickgzzjr @ashnfb Sorry for the multiple ping but since this issue remains quiet from any contributor's intervention I need to know how this issue can be improved to be valid.

Thanks a lot for your future help on this.

@ashnfb
Copy link
Contributor

ashnfb commented Jan 15, 2019

@jonathangreco do you have a git repo with your code for testing?

@cobarx
Copy link
Contributor

cobarx commented Jan 16, 2019

Hi sorry for not responding sooner. It is hard to keep up with the volume of issues that this project generates. I don't get to put in much time on this project for my work, so it's mostly in my free time and there isn't much activity from other maintainers to help with the load. I end up putting most of my time into reviewing PRs.

However, since this is a crash it needs to get fixed asap. I should be able to take a look at this this week and see if I can sort it out. If you happen to be able to put together a PR that would be great, but I should be able to tackle it.

@jonathangreco
Copy link
Author

jonathangreco commented Jan 16, 2019

@ashnfb Hi, I've not any git repo for testing at the moment. But maybe this snack https://snack.expo.io/@jonathan01/iphone-fullscreen can be mounted as a project on IOS to test it. (Snack not compile react-native-video, i can't make it work)

@cobarx Don't be sorry, no problem, you may notice that I'm a patient guy,I completely understand how it's complicated to handle a community repository in addition of your work, I'm glad to see you in this thread now :)

I'm not an objective C nor a Java developper sorry so I can't make a PR in order to fix this.

It seems that it's easy to reproduce, because a lot of people came on this thread to confirm this is still happening.

I hope you'll be able to reproduce easily.

@ashnfb
Copy link
Contributor

ashnfb commented Jan 16, 2019

@cobarx @jonathangreco if you can wait until early next week, I will try and put together a PR for this Monday or Tuesday

@cobarx
Copy link
Contributor

cobarx commented Jan 19, 2019 via email

@ashnfb
Copy link
Contributor

ashnfb commented Jan 21, 2019

Example code is at this branch:
https://github.com/nfb-onf/react-native-video/tree/1319-fullscreen-rotation-issues

Confirmed there are issues when controls={true}. Doesn't happen if controls are set to false.

@jonathangreco
Copy link
Author

Then it's confirmed, happy here :)

@ashnfb
Copy link
Contributor

ashnfb commented Jan 22, 2019

@jonathangreco I'm working on it. It looks like react-native is not getting onLayout (rotation changes) when in full-screen mode via the built-in controls, so when exiting it messes up the layout. Might need a hack to fix.

@ashnfb
Copy link
Contributor

ashnfb commented Jan 22, 2019

@jonathangreco @cobarx fixed
PR #1441
Took about 2 days :(

I'm not very happy with the fix; but it's the best I could come up with.

Here's the diagnosis and solution:

Normally, whenever we rotate the device, react-native components receive an "onLayout" event.

In this code, there are 2 different methods for rendering video: AVPlayerLayer (doesn't have controls), and an embedded AVPlayerViewController (when controls are on). The problem is when AVPlayerViewController is in fullscreen and a rotation occurs, the "onLayout" is never sent to react-native's shadowViews.

The solution is to force the reactViewController.view (the rootViewController) to update it's bounds to the rotated screen's bounds and mark the view as needing layout. This causes the right sequence of events -- ie. react-native calls onLayout correctly and layouts out all other subviews.

Couple of other notes. The basic example, isn't very basic. I've added a new example which is really barebones, and necessary for testing bugs. Feel free to rename it in the PR as appropriate.

@jonathangreco
Copy link
Author

jonathangreco commented Jan 23, 2019

whoaaa really thanks for the fix.
For what it's worth, I really appreciate your work, even if the fix not makes you happy.

@jonathangreco
Copy link
Author

@ashnfb Hi is this planned to be merged soon ?

@ashnfb
Copy link
Contributor

ashnfb commented Feb 15, 2019 via email

@jonathangreco
Copy link
Author

jonathangreco commented Feb 21, 2019

Hi @cobarx what do you need on this issue in order to merge this ? It would be nice if I can get this for my deployment of my app on the apple store next week :)

Pleeeease :)

@fubar
Copy link

fubar commented Mar 4, 2019

I'm experiencing the same issue. Looking forward to the fix.

@jonathangreco
Copy link
Author

@cobarx another ping from here, please do your best on this.

@cobarx
Copy link
Contributor

cobarx commented Mar 13, 2019

I'm not having the best of luck reproducing this, but I trust @ashnfb's judgment on this and it sounds like the fix works for you @jonathangreco so I went ahead and merged it.

I did find one issue with the fix. If I go fullscreen using the button on the onscreen controls, everything works fantastic. However if I use this.video.presentFullscreenPlayer(), it often produces the following red screen:
FS Error

@ashnfb would you mind looking and seeing if you can diagnose this?

@cobarx
Copy link
Contributor

cobarx commented Mar 13, 2019

When I test going fullscreen via the method on 4.4.0 it doesn't produce the red screen, however the controls are the wrong size and in the wrong position.

@fubar
Copy link

fubar commented Mar 21, 2019

@cobarx @ashnfb what's the status on this? When is it going to be released? Thanks!

@ashnfb
Copy link
Contributor

ashnfb commented Mar 26, 2019 via email

@ashnfb
Copy link
Contributor

ashnfb commented Jun 3, 2019

Hi @jonathangreco, I think the problem is the scrollview needs a different set of styling to resize correctly in a parent view. The way I discovered the issue was giving all the views a different background color, and I was able to see the scrollview wasn't resizing. I haven't explored a solution, but I'll try and have a look at it this week.
cheers

@jonathangreco
Copy link
Author

@ashnfb Hope you can do it, really thank you for your work, I will be able to test anything you'll push on my test repo

@ashnfb
Copy link
Contributor

ashnfb commented Jun 10, 2019

@jonathangreco just dropping an update. I haven't been able to fix this with a scrollview yet. Normally onLayout is called when a view has a flex=1. This continues to work with Views and fullscreen, however, when a ScrollView is involved, exiting Fullscreen stops the flex=1 from triggering an onLayout change, which results in the layout being incorrect. It seems to be a rendering issue deeper in the React code; and I'm not sure there is a easy workaround. Maybe @cobarx can help us?

@jonathangreco
Copy link
Author

jonathangreco commented Jun 12, 2019

I don't know if @cobarx can help.

I'm only able to test your work on this one. I'm not a native developper yet, but a simple react-native user :)

I can jus hope that this isssue will find an happy ending, since it's been a 8 month now. My app does not look the same without fullscreen. It's missing the "Wahouuu" effect ;p

Again many thanks to you for the time passed on this.

@CHaNGeTe
Copy link
Contributor

I don't know if it helps you, but I am using https://www.npmjs.com/package/react-native-orientation-locker for that.

When the component is unmounted or when navigation occurs, I set the orientation lock back to the value I need.

@quachsimon
Copy link

I am also experiencing this issue where the layout does not change back to portrait after rotating it to landscape. However, I noticed it does NOT happen on iPhone X (and any other screens with a notch) but, on the iPhone 6/7/8 it experiences this.

Could this be a device related issue?

@pisacode
Copy link

@jonathangreco just dropping an update. I haven't been able to fix this with a scrollview yet. Normally onLayout is called when a view has a flex=1. This continues to work with Views and fullscreen, however, when a ScrollView is involved, exiting Fullscreen stops the flex=1 from triggering an onLayout change, which results in the layout being incorrect. It seems to be a rendering issue deeper in the React code; and I'm not sure there is a easy workaround. Maybe @cobarx can help us?

Do you have an idea about the fix to this issue? @cobarx

@ashnfb
Copy link
Contributor

ashnfb commented Jul 3, 2019

@jonathangreco I now have this working correctly! @CHaNGeTe was correct, and this is an orientation problem we're dealing at the react-native level, when re-rendering the layout of views. There are no code changes to react-native-video, it simply needs re-rendering on the js side - which you can do with a package, or listen to a change in Dimensions.

I've attached an example for you here
https://jsfiddle.net/nunavut/vpzLg0ac/

p.s. Make sure to use my fork to test. https://github.com/nfb-onf/react-native-video

@jonathangreco
Copy link
Author

jonathangreco commented Jul 4, 2019

@ashnfb I tested it on an Iphone XR, the content is now centered when back to the app (exit fullscreen) but there still a white side...unusable. The rendering do its job, centered the content again. But the app use the half of the screen after rotating.

So there is progress but it's still unusable :p

I paste below my code of the screen, I also tested if your snippet, same issue.

I did a yarn upgrade to pull your last commit from your repo, but no luck.

import React, {Component} from 'react';
import {Button, FlatList, ScrollView, StyleSheet, Text, View, Dimensions} from 'react-native';
import VideoComponent from "../component/VideoComponent";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
import * as defaultActions from "../actions/defaultActions";
import * as velibActions from "../actions/velibActions";
import Icon from 'react-native-vector-icons/FontAwesome';


class MainScreen extends Component {

  constructor(props) {
    super(props);
    this.state = {
      width: Dimensions.get('window').width,
      height: Dimensions.get('window').height,
      videoStatus: "Not ended",
    };

    Dimensions.addEventListener("change", (e) => {
      this.setState(e.window);
    });
  }

  fetchData = () => {
    let results = fetch(
      "https://opendata.paris.fr/api/records/1.0/search/?dataset=velib-disponibilite-en-temps-reel&facet=overflowactivation&facet=creditcard&facet=kioskstate&facet=station_state"
    );

    results.then(response => {
      response
        .json()
        .then(data => {
          this.setState({
            data: data
          });
        });
    });
  };

  componentDidMount() {
    this.fetchData();
    this.props.actions.acceptCgu(true);
  }

  renderRow = ({item}) => {
    let isBookmarked = (this.props.velib_bookmarked.find(velib => velib.id === item.recordid));
    let jsx = <Icon.Button
      name="plus"
      size={20}
      iconStyle={{marginRight: 0}}
      backgroundColor="green"
      onPress={() => this.props.velibActions.addToBookmark({id : item.recordid, station_name: item.fields.station_name}) }
    />;

    if (isBookmarked) {
      jsx = <Icon.Button
        name="minus"
        size={20}
        iconStyle={{marginRight: 0}}
        backgroundColor="red"
        onPress={() => this.props.velibActions.removeFromBookmark(item.recordid) }
      />
    }
      return (
        <View style={styles.rowItem}>
          <View style={styles.icon}>
            {jsx}
          </View>
          <Text>{item.fields.station_name}</Text>
        </View>
      );
  };

  onVideoEnd = (uri) => {
    this.setState({
      videoStatus: "Vidéo terminée! Avez vous aimé : " + uri
    })
  };

  render() {
    return (
      <View style={{flex: 1, backgroundColor: 'red', width: this.state.width, height:this.state.height, alignItems: 'center',
        justifyContent: 'center',}}>
        <ScrollView vertical style={{flex: 1, backgroundColor: 'yellow'}}
                    onLayout={() => console.log('rendering scrollview')}>
            <Text style={styles.welcome}>Welcome to React Native!</Text>
            <VideoComponent
              paused={true}
              onVideoEnd={(uri) => this.onVideoEnd(uri)}
              size={30}
              controls={true}
              uri={"https://app.dollycast.io/films/102/maestro_product-1551264704/movie_maestro_product-1551264704_1.mp4"}
            />
            {this.state.data && <FlatList
              data={this.state.data.records}
              renderItem={(item) => this.renderRow(item)}
              keyExtractor={(item) => "" + item.recordid}
              style={styles.listContainer}
              extraData={this.props.velib_bookmarked}
            />}

            <Text style={styles.welcome}>{this.state.videoStatus}</Text>

            <Button
              title="Go to Home"
              onPress={() => this.props.navigation.navigate('HomeScreen')}
            />

            <Button
              title="Go to Flex"
              onPress={() => this.props.navigation.navigate('FlexScreen')}
            />
        </ScrollView>
      </View>
    );
  }
}

function mapStateToProps(state) {
  return {
    user: state.defaultReducer.user,
    cgu_accepted: state.defaultReducer.cgu_accepted,
    velib_bookmarked: state.velibReducer.velib_bookmarked
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(defaultActions, dispatch),
    velibActions : bindActionCreators(velibActions, dispatch)
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MainScreen);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  listContainer: {

  },
  rowItem: {
    flexDirection: "row",
    flex: 1,
    height: 50,
    justifyContent: "space-between",
    alignItems: "center",
  },
  icon: {
    marginRight: 10
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});


@ashnfb
Copy link
Contributor

ashnfb commented Jul 4, 2019

@jonathangreco I was also getting the 1/2 white; it's a bit tricky because scrollview is quite broken. Send me your project by email at [email protected]

@CHaNGeTe CHaNGeTe changed the title fullscreen mode on IOS crash layout when video ends fullscreen mode on IOS breaks layout when video ends Jul 5, 2019
@jonathangreco
Copy link
Author

jonathangreco commented Jul 9, 2019

From emails between @ashnfb and myself it appears that React-navigation can cause the 1/2 white screen.

But remove it (since it's a major components) does not seems to be a good idea, a package wich is incompatible with another is bad and have to be fixed from one side or the other.

But even if the white screen can be fixed for now by removing the navigation system, we still have the issue when the video finishes (the root view is destroyed ?)
Also, there still exist a scroll view issue when after rotates occurred the scrool view is blocked

So far, I don't think this workaround is fully stable, it needs more investigation and work around the component ScrollView.

Maybe we can post an issue on react-native main package for the scroll view component ?
We can also post an issue on react-navigation package if we are sure that it's on their side (for the white screen)

@cobarx since you have a lot to catch up on this issue, I understand you don't want to involve, but we are really stuck on this at the moment, even if @ashnfb did a really great job to investigate on this, your help would be a great thing.

We are near the solution, I'm sure we are

@fubar
Copy link

fubar commented Jul 23, 2019

In case this helps anyone: I have tried https://github.com/abbasfreestyle/react-native-af-video-player as well as Expo's video module (https://docs.expo.io/versions/latest/sdk/video/) and they both have the exact same issue. Some debugging has lead me to react-navigation-stack's StackViewLayout.js file; all of this in addition to previous comments support the conclusion that it's a lower-level issue and probably isolated to React Navigation.

I ended up implementing a custom controls interface on top of Expo's video module so that I can prevent fullscreen mode. This turned out to be a much bigger undertaking than I had hoped for but at least it's a somewhat acceptable UX. I started with https://github.com/ihmpavel/expo-video-player for the controls interface but ended up copying and heavily modifying its code to get it to an acceptable level of UX as it's rather, well, quirky out of the box, and has some bugs and outdated dependencies. If anyone is interested I'd be happy to share my modified version.

@jonathangreco
Copy link
Author

I discovered a new thing about this issue, this can maybe give a boost to its resolve.
Look below a demo :
https://youtu.be/tYxY1WJkuO8

@ashnfb @cobarx @CHaNGeTe any hint about this now ?

@jonathangreco
Copy link
Author

@fubar Hi, I didn't take the time to answer your post, what you did is pretty amazing, if I understand it correctly you have made native controls on iOS that not show the fullscreen button ?

If so can you make a PR in this repo to allow via a props to remove the fullscreen button ?

@marlonandrade
Copy link
Contributor

Hey folks, arrived on this issue after trying to troubleshoot an issue on my app on which just displaying video controls when not fullscreen would break the content size of scroll views when the react native view controller is not the whole screen size.

Shouldn't the logic that observes the frame on contentOverlayView and updates the reactViewController frame to the screen bounds to happen only when the video is fullscreen?

There's an if on the code over there that checks wether its full screen or not, but it only logs it. Applying this logic only on the fullscreen solves the problem I'm having on my App, but I couldn't verify if it still solves the other cases the workaround was intended to solve.

Cheers o/

@hedgerh
Copy link

hedgerh commented Apr 15, 2020

@marlonandrade I stumbled on this issue and your PR #1931

This seems to still break my layout (uses tab navigation) when going into fullscreen then back. Would it make sense to add some logic to the "not fullscreen" part to return the view back to its original size?

Don't know Obj C that well to know what it'd look like in code, unfortunately.

@marlonandrade
Copy link
Contributor

Hi @hedgerh, indeed there's no code path that restores the original size of the original react view and this will be a problem on my App as well.

I'm not very familiar with the whole view hierarchy and its lifecycle, so dunno if this code path will get executed when getting back from fullscreen to be able to restore it.

If u wanna give it a try, a quick hack to check it would be to save the original self.reactViewController.view.frame before the first time its set to the screen bounds and trying to restore it afterwards.

Unfortunately I don't have time right now to dedicate to that =(

@hedgerh
Copy link

hedgerh commented Apr 15, 2020

Fyi if you need a hack, I ended up getting things to work by removing the reactViewController frame resize, then insstalling react-native-orientation and adding these handlers to my <Video> component.

onFullscreenPlayerWillPresent={() => {
  Orientation.unlockAllOrientations()
}}
onFullscreenPlayerWillDismiss={() => {
  Orientation.lockToPortrait();
}}

If I get a chance I may mess with the actual frame resize, but this may have to do.

@KestasVenslauskas
Copy link

any update on this?

@alex-a-pereira
Copy link

A little late here but just in case anybody hits a similar issue as me.. I had a similar issue because I was setting a wrapping view style with width: '100%'. The fix was to change the parent view to be set to a static width.

I know this doesn't fit all cases (even for us) but hopefully this saves somebody from spending an hour debugging :)

My component tree looked like:

// assume that we don't have control of width of view that renders 
// a MyVideoPlayer component
const MyVideoPlayer = (props) => {
  return (
    <View style={styles.container}>
      <Video style={styles.videoPlayer} {...props} />
    </View>
  )
}

const styles = EStyleSheet.create({
  container: {
    height: 200,
    width: '100%' // this breaks!
  },
  videoPlayer: { flex: 1 }
})

The fix:

const styles = EStyleSheet.create({
  container: {
    height: 200,
    width: Dimensions.get('window').width
  },
  videoPlayer: { flex: 1 }
})

@cristianoccazinsp
Copy link

Any ideas how to fix it when full screen video plays on React Native WebView ? Same issue as this library. I wonder if a RN bug.

@ayyunmufti
Copy link

Hi everyone
I was facing same issues just found out
Check if your app is wrapped with ant gesture handler provider please romove it and check
When app switches back to portrait that rerenders and blocks the view

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests