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

Property hasTVPreferredFocus has a 1 second delay on tvOS #23527

Closed
hufkens opened this issue Feb 19, 2019 · 11 comments
Closed

Property hasTVPreferredFocus has a 1 second delay on tvOS #23527

hufkens opened this issue Feb 19, 2019 · 11 comments
Labels
Bug Platform: tvOS tvOS applications.

Comments

@hufkens
Copy link

hufkens commented Feb 19, 2019

BughasTVPreferredFocus.zip

🐛 Bug Report

We are using React Native to build an app for all platforms including Apple TV.
We need the hasTVPreferredFocus property to show initial focus. But there seems to be a hard-coded delay of 1 sec in the code.

To Reproduce

Create a project for tvOS.
Add a <View> component with a couple of <TouchableHighlight> components. Set the hasTVPreferredFocus property of the last one to true.
The component will render but the last component will get the focus after a one second delay.

Expected Behavior

The last one should be focused in render, without the 1 second delay.

Code Example

const Test = () => (
  <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
    <TouchableHighlight tvParallaxProperties={{ magnification: 1.5 }}>
      <Text style={{ color: 'white', fontSize: 50 }}>Button1</Text>
    </TouchableHighlight>
    <TouchableHighlight tvParallaxProperties={{ magnification: 1.5 }}>
      <Text style={{ color: 'white', fontSize: 50 }}>Button2</Text>
    </TouchableHighlight>
    <TouchableHighlight
      hasTVPreferredFocus
      tvParallaxProperties={{ magnification: 1.5 }}
    >
      <Text style={{ color: 'white', fontSize: 50 }}>Button3</Text>
    </TouchableHighlight>
  </View>
);

Native code with the delay.

- (void)setHasTVPreferredFocus:(BOOL)hasTVPreferredFocus
{
  _hasTVPreferredFocus = hasTVPreferredFocus;
  if (hasTVPreferredFocus) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      UIView *rootview = self;
      while (![rootview isReactRootView] && rootview != nil) {
        rootview = [rootview superview];
      }
      if (rootview == nil) return;

      rootview = [rootview superview];

      [(RCTRootView *)rootview setReactPreferredFocusedView:self];
      [rootview setNeedsFocusUpdate];
      [rootview updateFocusIfNeeded];
    });
  }
}

Environment

  React Native Environment Info:
    System:
      OS: macOS 10.14.3
      CPU: (12) x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
      Memory: 7.79 GB / 32.00 GB
      Shell: 5.3 - /bin/zsh
    Binaries:
      Node: 10.14.2 - /usr/local/opt/node@10/bin/node
      Yarn: 1.9.4 - /usr/local/bin/yarn
      npm: 6.4.1 - /usr/local/opt/node@10/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 12.1, macOS 10.14, tvOS 12.1, watchOS 5.1
    IDEs:
      Android Studio: 3.1 AI-173.4907809
      Xcode: 10.1/10B61 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.6.3 => 16.6.3 
      react-native: 0.58.4 => 0.58.4 
    npmGlobalPackages:
      react-native-cli: 2.0.1
@react-native-bot react-native-bot added the Platform: tvOS tvOS applications. label Feb 19, 2019
@react-native-bot

This comment has been minimized.

@hufkens
Copy link
Author

hufkens commented Feb 21, 2019

Just removed the delay and everything seems to work fine and the app behaves now like it should. Any idea what I am missing?

- (void)setHasTVPreferredFocus:(BOOL)hasTVPreferredFocus
{
  _hasTVPreferredFocus = hasTVPreferredFocus;
  if (hasTVPreferredFocus) {
    dispatch_async(dispatch_get_main_queue(), ^{
      UIView *rootview = self;
      while (![rootview isReactRootView] && rootview != nil) {
        rootview = [rootview superview];
      }
      if (rootview == nil) return;

      rootview = [rootview superview];

      [(RCTRootView *)rootview setReactPreferredFocusedView:self];
      [rootview setNeedsFocusUpdate];
      [rootview updateFocusIfNeeded];
    });
  }
}

@ericlewis
Copy link
Contributor

@hufkens the code with the delay was like 2 years old, my guess is it was done in order to ensure the focus switched on re-renders. I will open a PR for removing the delay after I test a few more scenarios, such as switching focus during renders.

@hufkens
Copy link
Author

hufkens commented Feb 23, 2019

@ericlewis Thanks for the response. We are struggling a lot with focus and react-native. But looks like we are slowly getting there. I patched the code in our project so I'll let you know if we have any strange side-effects.

@kononenkoAnton
Copy link

kononenkoAnton commented Feb 27, 2019

Please update here when it will be released. We are waiting this fix too :)

@francois-codes
Copy link

It'd be interesting to understand why this was done in the first place - I assume this was not just for the fun of it :)

For now we've patched it as well, but it's clearly not an ideal solution

@hufkens
Copy link
Author

hufkens commented Feb 28, 2019

Probably because react-native doesn't use AutoLayout, but the FocusEngine works with the AutoLayout there could be a delay. Problem is that having a 1 second delay is really weird and unusable if you want to use it.

Also we miss the FocusGuide in react-native. The FocusGuide is a component that can be used to direct the focus from one component to another if there is no direct line of sight (up, down, left, right).

We found a way to directly set focus to a RCTView:

const RCTUIManager = NativeModules.UIManager;
RCTUIManager.updateView(tag, 'RCTView', {
    hasTVPreferredFocus: true,
 });

@francois-codes
Copy link

Nice one !

Have you tried using a ref and call this.refs[viewRef].setNativeProps({ hasTVPreferredFocus: true }} ? shouldn't it do the same thing ?

I see we're facing the same problems :) I agree the 1 second delay isn't usable, but if it's here for a reason, maybe we should not just trash it like that.
We're also trying to figure out how to leverage FocusGuide in React-Native. We'll probably create a NativeModule for that

@hufkens
Copy link
Author

hufkens commented Feb 28, 2019

@f-roland That would be awesome. If you need any help let me know.

sheck added a commit to sheck/react-native that referenced this issue Apr 12, 2019
Discovered on:
facebook#23527

This might be dumb to undo this, but everything seems to be working fine
for right now.
@respectTheCode
Copy link
Contributor

We have been avoiding hasTVPreferredFocus because of this issue. Is patching RN working?

@douglowder
Copy link
Contributor

This is fixed in the new community repo for tvOS (react-native-tvos/react-native-tvos#2), and is part of the first NPM release (react-native-tvos@0.59.8-0). See this doc for instructions.

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

No branches or pull requests

8 participants