Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) NSTimeInterval loadingViewFadeDuration;
@property (nonatomic, assign) CGSize minimumSize;

#if TARGET_OS_TV
@property (nonatomic, copy, nullable) NSArray<id<UIFocusEnvironment>> *reactPreferredFocusEnvironments;
@property (nonatomic, weak, nullable) UIView *reactPreferredFocusedView;
#endif

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ - (void)surface:(RCTSurface *)surface didChangeStage:(RCTSurfaceStage)stage
[super surface:surface didChangeStage:stage];
if (RCTSurfaceStageIsRunning(stage)) {
[_bridge.performanceLogger markStopForTag:RCTPLTTI];
#if TARGET_OS_TV
dispatch_async(dispatch_get_main_queue(), ^{
self.reactPreferredFocusedView = nil;
[self setNeedsFocusUpdate];
[self updateFocusIfNeeded];
});
#endif
}
}

Expand Down Expand Up @@ -154,4 +161,23 @@ - (void)cancelTouches
// Not supported.
}

#if TARGET_OS_TV
#pragma mark - UIFocusEnvironment

- (NSArray<id<UIFocusEnvironment>> *)preferredFocusEnvironments
{
if (self.reactPreferredFocusEnvironments != nil && self.reactPreferredFocusedView.window != nil) {
NSArray<id<UIFocusEnvironment>> *tempReactPreferredFocusEnvironments = self.reactPreferredFocusEnvironments;
self.reactPreferredFocusEnvironments = nil;
return tempReactPreferredFocusEnvironments;
}

if (self.reactPreferredFocusedView && self.reactPreferredFocusedView.window != nil) {
return @[ self.reactPreferredFocusedView ];
}

return [super preferredFocusEnvironments];
}
#endif

@end
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#import "RCTViewComponentView.h"
#import <React/RCTSurfaceHostingProxyRootView.h>

#import <CoreGraphics/CoreGraphics.h>
#import <QuartzCore/QuartzCore.h>
Expand Down Expand Up @@ -53,6 +54,7 @@ @implementation RCTViewComponentView {
BOOL _useCustomContainerView;
NSMutableSet<NSString *> *_accessibilityOrderNativeIDs;
RCTSwiftUIContainerViewWrapper *_swiftUIWrapper;
BOOL _focusable;
}

#ifdef RCT_DYNAMIC_FRAMEWORKS
Expand Down Expand Up @@ -481,6 +483,13 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
needsInvalidateLayer = YES;
}

// `focusable`
#if TARGET_OS_TV
if (oldViewProps.focusable != newViewProps.focusable) {
_focusable = (bool)newViewProps.focusable;
}
#endif

// `mixBlendMode`
if (oldViewProps.mixBlendMode != newViewProps.mixBlendMode) {
switch (newViewProps.mixBlendMode) {
Expand Down Expand Up @@ -1422,6 +1431,11 @@ - (BOOL)wantsToCooptLabel
return !super.accessibilityLabel && super.isAccessibilityElement;
}

- (BOOL)canBecomeFocused
{
return _focusable;
}

- (BOOL)isAccessibilityElement
{
if (self.contentView != nil) {
Expand Down Expand Up @@ -1671,6 +1685,8 @@ - (void)transferVisualPropertiesFromView:(UIView *)sourceView toView:(UIView *)d
}
}

#pragma mark - Focus Events

- (BOOL)canBecomeFirstResponder
{
return YES;
Expand All @@ -1689,18 +1705,43 @@ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
}
}

#if TARGET_OS_TV
/// Finds the containing RCTSurfaceHostingProxyRootView by walking up the view
/// hierarchy.
- (RCTSurfaceHostingProxyRootView *)containingRootView
{
UIView *view = self;
while (view != nil) {
if ([view isKindOfClass:[RCTSurfaceHostingProxyRootView class]]) {
return (RCTSurfaceHostingProxyRootView *)view;
}
view = view.superview;
}
return nil;
}
#endif

- (void)focus
{
[self becomeFirstResponder];

#if TARGET_OS_TV
RCTSurfaceHostingProxyRootView *rootView = [self containingRootView];
if (rootView == nil) {
return;
}

rootView.reactPreferredFocusedView = self;
[rootView setNeedsFocusUpdate];
[rootView updateFocusIfNeeded];
#endif
}

- (void)blur
{
[self resignFirstResponder];
}

#pragma mark - Focus Events

- (BOOL)becomeFirstResponder
{
if (![super becomeFirstResponder]) {
Expand All @@ -1727,6 +1768,30 @@ - (BOOL)resignFirstResponder
return YES;
}

#if TARGET_OS_TV

- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if (context.previouslyFocusedView == context.nextFocusedView) {
return;
}

if (context.nextFocusedView == self) {
if (_eventEmitter) {
_eventEmitter->onFocus();
}
} else {
if (_eventEmitter) {
_eventEmitter->onBlur();
}
}

[super didUpdateFocusInContext:context withAnimationCoordinator:coordinator];
}

#endif

@end

#ifdef __cplusplus
Expand Down
Loading