在子视图之外触摸时删除子视图

Posted

技术标签:

【中文标题】在子视图之外触摸时删除子视图【英文标题】:Remove subview when touched outside of subview 【发布时间】:2014-05-14 03:42:10 【问题描述】:

我的情况比列出的其他人要复杂一些。

我有一个占据大部分屏幕的 UITableView。 每行都会弹出一个包含更多配置文件信息的子视图。再次单击屏幕时,此子视图消失。这非常有效。

在导航栏中,我有一个按钮,它将显示一个小菜单。

- (IBAction)menuButtonClicked:(UIBarButtonItem *)sender 
    //If menuView exists and Menu button is clicked, remove it from view
    if (self.menuView) 
        self.tableView.userInteractionEnabled = true;
        [self.menuView removeFromSuperview];
        self.menuView = Nil;
    
    //Menu View doesn't exist so create it
    else 
        // Create the Menu View and add it to the parent view
        self.menuView = [[[NSBundle mainBundle] loadNibNamed:@"MenuView" owner:self 
           options:nil] objectAtIndex:0];
        self.menuView.layer.cornerRadius = 20.0f;
        self.menuView.layer.borderWidth = 3.0f;
        self.menuView.layer.borderColor = [UIColor whiteColor].CGColor;
        self.menuView.frame = CGRectMake(0, 64, self.menuView.frame.size.width,
                                     self.menuView.frame.size.height);

        UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] 
            initWithTarget:self action:@selector(singleTapGestureCaptured:)];
        [self.menuView addGestureRecognizer:singleTap];

        //Disable Selection of Profiles while Menu is showing
        self.tableView.userInteractionEnabled = false;

        //Add MenuView to View
        [self.view addSubview: self.menuView];
    


//Removed Sub Views from View when tapped
-(void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
    if(self.profileView)
        [self.profileView removeFromSuperview];
        self.profileView = Nil;
    
    if(self.menuView) 
        self.tableView.userInteractionEnabled = true;
        [self.menuView removeFromSuperview];
        self.menuView = Nil;
    

现在我想在再次单击菜单按钮时关闭此菜单(在上面的代码中工作)以及当用户触摸菜单并在 tableView 或导航栏上时。如果显示菜单,我不希望 tableView 显示它的配置文件子视图(在上面的代码中工作),而只是删除 menuView。如果我触摸 tableView,我无法让 menuView 消失。

谁能指出我正确的方向?

【问题讨论】:

听起来你几乎想要一个弹出框。你能发布一个草图来展示你想要的行为吗? 我已经得到了一个“弹出框”,但它是一个使用 NIB 子视图的 menuView。当用户触摸它之外时,我想删除子视图。 【参考方案1】:

制作一个新的透明覆盖视图,其大小可以覆盖整个屏幕。将您的 menuView 添加为叠加层的子视图,然后将叠加层添加为主窗口的子视图。在覆盖层上放置一个轻击手势识别器,轻击时将其关闭。

如果菜单视图上的按钮不起作用,您可能需要在手势识别器上将 cancelsTouchesInView 设置为 NO。

大概是这样(请原谅错别字,我没有编译这个):

- (void)showMenu

  self.overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  overlay.backgroundColor = [UIColor clearColor];

  self.menuView = /* code to load menuView */;

  [overlay addSubview:self.menuView];

  UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self 
                                                                        action:@selector(onSingleTap:)];
  tap.cancelsTouchesInView = NO;
  [overlay addGestureRecognizer:tap];

  [self.tableView.window addSubview:overlay];


- (void)handleSingleTap:(UITapGestureRecognizer *)sender

  [self.overlay removeFromSuperview];

您可能还想添加一个滑动手势识别器来关闭叠加层,因为有人可能会尝试滚动表格并期望菜单被关闭。

【讨论】:

【参考方案2】:

在尝试使用我自己的自定义 NIB 文件制作我自己的自定义 topdown-slide 菜单时,我发现这可以通过许多技术来实现。我想建议并分享一个非常相似的不同解决方案,但它是在背景上使用自定义按钮创建的。 我一直在环顾四周,但找不到提到这一点的答案。 这与点击识别器非常相似,除了一件事 - 点击识别器遍布整个布局(包括子视图),而使用自定义按钮层允许您与顶视图交互并在单击时将其从超级视图中关闭/删除下层(当下层是背景按钮时)。我就是这样做的:

    您创建布局 您将 UIButtonTypeCustom 类型的 UIButton 添加到布局中 您将此布局框在您希望响应该点击/单击的视图上

    您将您的菜单视图添加到该布局的顶部并动画显示您的菜单

    - (void)showMenuViewWithBackgroundButtonOverlay
        
            self.backgroundButton = (
                UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
                button.frame = self.view.frame;
                [button addTarget:self action:@selector(toggleAppMenu) forControlEvents:UIControlEventTouchUpInside];
                button;
            );
    
            if (!self.menu) 
                self.menu = [self createMenu]; // <-- get your own custom menu UIView
            
            if (!self.overlay) 
                self.overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
                self.overlay.backgroundColor = [UIColor clearColor];
    
                [self.overlay addSubview:self.backgroundButton];
    
                [self.overlay addSubview:self.menu];
    
                [self.view addSubview:self.overlay];
            
    
            [self toggleAppMenu];
        
    

还有toggleAppMenu:

- (void)toggleAppMenu

    CGRect nowFrame = [self.menu frame];
    CGRect toBeFrame = nowFrame;
    CGFloat navHeight = self.navigationController.navigationBar.frame.size.height;
    CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;

    if (self.showingMenu) 
        toBeFrame.origin.y = toBeFrame.origin.y-nowFrame.size.height-navHeight-statusBarHeight;
        [UIView animateWithDuration:0.5 animations:^
            [self.menu setFrame: toBeFrame];
        completion:^(BOOL finished) 
            self.showingMenu = !self.showingMenu;
            [self.view endEditing:YES];

            [self.overlay removeFromSuperview];
            self.overlay = nil;
            NSLog(@"menu is NOT showing");
        ];
    
    else
        toBeFrame.origin.y = navHeight+statusBarHeight;

        [UIView animateWithDuration:0.5 animations:^
            [self.menu setFrame: toBeFrame];
        completion:^(BOOL finished) 
            self.showingMenu = !self.showingMenu;
            NSLog(@"menu is showing");
        ];

    

我希望这对某人有所帮助。

【讨论】:

【参考方案3】:

适用于 Swift 5

我创建了自定义视图,我希望通过点击子视图外部来隐藏它。也许它可以帮助某人或任何人都可以提出更好的方法:)

// create tap for view
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(animateOut))
        self.addGestureRecognizer(tapGesture)
        
 // create tap for subview
        let tapGesture2 = UITapGestureRecognizer(target: self, action: nil)
        container.addGestureRecognizer(tapGesture2)

【讨论】:

以上是关于在子视图之外触摸时删除子视图的主要内容,如果未能解决你的问题,请参考以下文章

防止父视图在子视图作用于它后接收触摸事件

如何在子视图和父视图组触摸事件之间变化

根 UIViewController 方向横向,在子视图中触摸奇怪

添加、删除子视图并在子视图和主 uiviewcontroller 之间进行通信

删除子视图时如何修复触摸禁用?

UIView clipToBounds 没有停止子视图接收父视图之外的触摸