Restore first responder of view controller when screen reactivates (#48)

This change automates first responder restoring when screen is deactivated and the activated back (e.g. when we push new screen on top and then go back). In addition we disable pointerEvent setting for the Screen component as changes made to that prop would cause underlying views to resign responder before we can remember it. Because of that we add an automatic handling for pointer events for the Screen component that disables all touch interactions when screen is transitioning.
This commit is contained in:
Krzysztof Magiera
2018-12-20 08:44:01 +01:00
committed by GitHub
parent 5dadab9aa6
commit 5893d9a8a7
3 changed files with 67 additions and 11 deletions

View File

@@ -13,6 +13,8 @@
@property (nonatomic, retain) UIViewController *controller;
@property (nonatomic) BOOL active;
- (void)notifyFinishTransitioning;
@end
@interface UIView (RNSScreen)

View File

@@ -4,10 +4,15 @@
@interface RNSScreen : UIViewController
- (instancetype)initWithView:(UIView *)view;
- (void)notifyFinishTransitioning;
@end
@implementation RNSScreenView
@implementation RNSScreenView {
RNSScreen *_controller;
}
@synthesize controller = _controller;
- (instancetype)init
{
@@ -37,10 +42,16 @@
_controller = nil;
}
- (void)notifyFinishTransitioning
{
[_controller notifyFinishTransitioning];
}
@end
@implementation RNSScreen {
__weak UIView *_view;
__weak id _previousFirstResponder;
}
- (instancetype)initWithView:(UIView *)view
@@ -51,6 +62,36 @@
return self;
}
- (id)findFirstResponder:(UIView*)parent
{
if (parent.isFirstResponder) {
return parent;
}
for (UIView *subView in parent.subviews) {
id responder = [self findFirstResponder:subView];
if (responder != nil) {
return responder;
}
}
return nil;
}
- (void)willMoveToParentViewController:(UIViewController *)parent
{
if (parent == nil) {
id responder = [self findFirstResponder:self.view];
if (responder != nil) {
_previousFirstResponder = responder;
}
}
}
- (void)notifyFinishTransitioning
{
[_previousFirstResponder becomeFirstResponder];
_previousFirstResponder = nil;
}
- (void)loadView
{
self.view = _view;

View File

@@ -109,22 +109,35 @@
}
}
// add new screens in order they are placed in subviews array
for (RNSScreenView *screen in _reactSubviews) {
if (screen.active && ![_activeScreens containsObject:screen]) {
[_activeScreens addObject:screen];
[self attachScreen:screen];
} else if (screen.active && activeScreenAdded) {
// if we are adding new active screen, we perform remounting of all already marked as active
// this is done to mimick the effect UINavigationController has when willMoveToWindow:nil is
// triggered before the animation starts
if (activeScreenAdded) {
// if we are adding new active screen, we perform remounting of all already marked as active
// this is done to mimick the effect UINavigationController has when willMoveToWindow:nil is
// triggered before the animation starts
if (activeScreenAdded) {
for (RNSScreenView *screen in _reactSubviews) {
if (screen.active && [_activeScreens containsObject:screen]) {
[self detachScreen:screen];
// disable interactions for the duration of transition
screen.userInteractionEnabled = NO;
}
}
// add new screens in order they are placed in subviews array
for (RNSScreenView *screen in _reactSubviews) {
if (screen.active) {
[self attachScreen:screen];
}
}
}
// if we are down to one active screen it means the transitioning is over and we want to notify
// the transition has finished
if (activeScreenRemoved && _activeScreens.count == 1) {
RNSScreenView *singleActiveScreen = [_activeScreens anyObject];
// restore interactions
singleActiveScreen.userInteractionEnabled = YES;
[singleActiveScreen notifyFinishTransitioning];
}
if ((activeScreenRemoved || activeScreenAdded) && _controller.presentedViewController == nil) {
// if user has reachability enabled (one hand use) and the window is slided down the below
// method will force it to slide back up as it is expected to happen with UINavController when