Skip to content
Merged
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
55 changes: 49 additions & 6 deletions ios/MenuView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ @implementation MenuView {
UIColor *_textColor;
UIColor *_checkedColor;
UIColor *_uncheckedColor;
BOOL _isChildViewButton;
NSHashTable<UIView *> *_disabledViews;
}

+ (ComponentDescriptorProvider)componentDescriptorProvider
Expand All @@ -31,6 +33,8 @@ - (instancetype)initWithFrame:(CGRect)frame
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const MenuViewProps>();
_props = defaultProps;
_disabledViews = [NSHashTable weakObjectsHashTable];
_isChildViewButton = NO;
}

return self;
Expand All @@ -42,10 +46,8 @@ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childCompone
if (index == 0) {
// Clean up old child view if exists
if (_childView && _childView != childComponentView) {
// Remove from our manual tracking without calling removeFromSuperview
// since React will handle the view hierarchy cleanup
_childView = nil;
_menuButton = nil;
[self cleanupMenuButton];
[self restoreUserInteractionForDisabledViews];
}

_childView = (UIView *)childComponentView;
Expand All @@ -64,8 +66,9 @@ - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childCompo
{
// Clean up our references before React unmounts
if (index == 0 && _childView == childComponentView) {
[self cleanupMenuButton];
[self restoreUserInteractionForDisabledViews];
_childView = nil;
_menuButton = nil;
}

// Let React handle the unmounting
Expand All @@ -78,9 +81,11 @@ - (void)setupChildViewAsMenuTrigger:(UIView *)childView
if ([childView isKindOfClass:[UIButton class]]) {
_menuButton = (UIButton *)childView;
_menuButton.showsMenuAsPrimaryAction = YES;
_isChildViewButton = YES;
[self updateMenuItems:_menuItems selectedIdentifier:nil];
} else {
// For non-button children, create an invisible button overlay to show the menu
_isChildViewButton = NO;
[self disableUserInteractionRecursively:childView];

// Create an invisible button that covers the entire view
Expand All @@ -105,12 +110,50 @@ - (void)setupChildViewAsMenuTrigger:(UIView *)childView

- (void)disableUserInteractionRecursively:(UIView *)view
{
view.userInteractionEnabled = NO;
if (view.userInteractionEnabled) {
[_disabledViews addObject:view];
view.userInteractionEnabled = NO;
}
for (UIView *subview in view.subviews) {
[self disableUserInteractionRecursively:subview];
}
}

- (void)restoreUserInteractionForDisabledViews
{
// Create a copy of the objects to iterate over since NSHashTable with weak references
// can have objects deallocated during iteration
NSArray<UIView *> *viewsToRestore = [_disabledViews allObjects];

for (UIView *view in viewsToRestore) {
// The view might have been deallocated (weak reference), so check if it's still valid
if (view && view.superview != nil) {
view.userInteractionEnabled = YES;
}
}
[_disabledViews removeAllObjects];
}

- (void)cleanupMenuButton
{
if (_menuButton) {
// If it's not a child view button (i.e., it's our overlay button), remove it
if (!_isChildViewButton && _menuButton.superview == self) {
[_menuButton removeFromSuperview];
}
// Clear the menu to prevent any lingering references
_menuButton.menu = nil;
_menuButton = nil;
}
_isChildViewButton = NO;
}

- (void)dealloc
{
[self cleanupMenuButton];
[self restoreUserInteractionForDisabledViews];
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<MenuViewProps const>(_props);
Expand Down
Loading