【问题标题】:Cancel out of UISearchBar when user taps on view当用户点击视图时取消 UISearchBar
【发布时间】:2016-03-06 13:48:10
【问题描述】:

当您点击搜索文本框下方和键盘上方时,有谁知道如何取消(退出 First Responder)的 UISearchBar?任何人都可以帮助发布一些代码来处理这个问题吗?

谢谢

【问题讨论】:

    标签: iphone objective-c uisearchbar


    【解决方案1】:

    在(UISearchbar 的)父视图中添加点击手势

    [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:searchBar action:@selector(resignFirstResponder)]];
    

    【讨论】:

    • 如果selfUIView,则此代码有意义。但大多数时候,self 实际上是一个UIViewController,所以这应该是[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self.searchBar action:@selector(resignFirstResponder)]];
    【解决方案2】:

    我通过使用 UITapGestureRecognizer 实现了这一点:

    UIGestureRecognizer* cancelGesture;
    
    - (void) backgroundTouched:(id)sender {
        [self.view endEditing:YES];
    }
    
    #pragma mark - UISearchBarDelegate
    
    -(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
        cancelGesture = [UITapGestureRecognizer new];
        [cancelGesture addTarget:self action:@selector(backgroundTouched:)];
        [self.view addGestureRecognizer:cancelGesture];
    }
    
    -(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
        if (cancelGesture) {
            [self.view removeGestureRecognizer:cancelGesture];
            [cancelGesture release];
            cancelGesture = nil;
        }
    }
    

    代码很简单,但您可以看到意图。当 SearchBar 开始编辑时,您将点击手势识别器附加到视图控制器的视图,并在停止编辑时将其移除。

    您可以解决一些注意事项:这样做会导致如果您点击键盘或搜索栏文本字段之外的任何内容,识别器会捕获点击 - 所以如果您使用清除、取消、范围或结果按钮,他们将无法正确响应。

    在我的特定场景中,我有一个 UITableView 覆盖了视图的暴露区域,因此我将手势识别器而不是视图控制器的主视图附加到它,从而隔离了手势响应的区域。

    【讨论】:

      【解决方案3】:

      我从iphonedevbook(示例代码项目 04)中得到的另一个想法是使用一个位于所有其他控件后面的大透明按钮,如果被点击,它只会让所有第一响应者辞职。 IE。如果用户在没有更重要的控制(这是直观的行为)的任何地方点击,搜索栏和键盘就会消失。

      【讨论】:

      • 这实际上与我上面的建议相同。实现起来可能会简单一些。
      • 谢谢豪克!创建透明按钮并处理 touchup 事件以让第一响应者辞职就可以了。
      • 我现在遇到的另一个问题是,假设我有一个包含 mkmapview 的视图,位于透明按钮后面。搜索栏消失后如何重新激活地图?
      • 我想它不再相关了——但对于其他感兴趣的人:您可以使用 UITapGestureRecognizer 检测地图上的点击,然后您根本不需要使用按钮:UITapGestureRecognizer *taprec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)]; [地图 addGestureRecognizer:taprec];
      【解决方案4】:

      我最终使用了 Hauke 和 Beau Scott 的混合方法。我在使用他们的解决方案时遇到了两个问题:

      1) 如果屏幕上还有其他内容,点击它不会导致调用 resignFirstResponder。例如,如果用户点击一个按钮而不是按钮周围的空间,则该按钮将吞噬该事件。然而,Beau Scott 的解决方案解决了这个问题。

      2) 点击搜索栏本身将导致 resignFirstResponder 被调用。显然,当您点击 UISearchBar 时,您不希望键盘消失。下面描述的一个小改动解决了这个问题。

      我最终将我的视图设置如下。父视图有两个子视图 - UISearchBar 和一个包含其余 UI 元素的子视图。子视图占据了 UISearchBar 下方的整个屏幕。然后我使用 Beau Scott 的确切代码来添加和删除手势识别器,但我没有将其添加到 self.view 中,而是将其添加到了子视图中:

      IBOutlet UIView *gestureRecognizer;
      

      ...

      -(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
          cancelGesture = [UITapGestureRecognizer new];
          [cancelGesture addTarget:self action:@selector(backgroundTouch:)];
          [gestureRecognizer addGestureRecognizer:cancelGesture];
      }
      
      -(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
          if (cancelGesture) {
              [gestureRecognizer removeGestureRecognizer:cancelGesture];
              [cancelGesture release];
              cancelGesture = nil;
          }
      }
      

      【讨论】:

        【解决方案5】:

        首先,您需要对搜索栏的引用。假设您的控制器对象有一个对象引用 UISearchBar *theSearchBar,并且您在创建 UISearchBar 对象时分配它。

        接下来,您需要检测包含视图是否已被触摸。被触摸的视图“知道”,但您需要将该信息传递给控制器​​。遗憾的是,Apple 并没有提供一种简单的方法来做到这一点,但也没有那么难。

        我的解决方案是用一个 UIControl 替换 UIViewController 对象通常创建的标准 UIView,然后让 UIViewController 响应触摸事件。

        MainController.m

        - (void) loadView {
            UIControl *control = [[UIControl alloc] initWithFrame: <desired frame>];
        [control addTarget: self action: @selector(touchUpInside) 
                     forControlEvents: UIControlEventTouchUpInside];
               // or touch down events, or whatever you like
            self.view = control;
            [control release];
        }
        
        - (void) viewDidLoad {
            [super viewDidLoad];
            theSearchBar = [[UISearchBar alloc] initWithFrame: <desired frame>];
        
            // insert code to finish customizing the search bar
        
            [self.view addSubview: theSearchBar];
        }
        
        - (void) touchUpInside {
            if [theSearchBar isFirstResponder] {
                // grab any data you need from the search bar
                [theSearchBar resignFirstResponder];
            }
        }
        

        MainController.h

        @interface MainController : UIViewController
        {
            UISearchBar *theSearchBar;
        }
        

        澄清:

        只有一个对象——我们称之为 MainController 类——它是 UIViewController 的子类。上面列出的所有方法都在 MainController 中实现。 theSearchBar 在 .h 文件中被声明为 UISearchBar*。

        您是否使用 Interface Builder 定义视图和控制器?如果是这样,我建议你学习如何不使用它——一旦你掌握了我们在这里讨论的那种技巧,它就变成了障碍而不是帮助——我根本不使用它,永远。

        【讨论】:

        • 如何从 touchUpInside 事件中获取对 searchBar 的引用?我在包含搜索栏的 viewcontroller 类中有 touchUpInside。
        • 你的意思是,为了调用resignFirstResponder?如果您在创建它时将其保存为参考,则您已经拥有它。
        • 我有点困惑。上面的代码是否应该位于与我创建搜索栏的视图控制器不同的视图控制器中?另外,为什么在 loadView 例程中创建 UIControl 而不是 UISearchBar?
        • 是的,你很困惑,可能是因为我没有为你输入足够的代码。我将编辑我的回复。
        • 感谢您提供更多详细信息。我尝试非常具体地跟踪您的更新,但现在我收到错误:错误:'searchBar' undeclared(在此函数中首次使用)。仅供参考,searchBar 是在我的 viewDidLoad 事件中声明的 UISearchBar *searchBar 的名称。是否需要全局声明 searchBar?
        【解决方案6】:

        @Gia Dang 的回答是最简单的,但我没有继承 UIView,只有 UIViewController,所以我的调用略有不同。另外,由于我不知道实际调用resignFirstResponder 的开销,所以我更愿意先检查一下。这是更多的代码,但由于所有这些都是在主线程上完成的(这会降低 UI 的速度),我宁愿先检查一下。

        @implementation MyController : UIViewController {
        @private
            UISearchController *_uiSearchController;
        }
        
        - (void)viewDidLoad {
            // add tap on view to resign the responder if we're in the middle of typing in the search
            UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeKeyboardIfNeeded)];
            [self.view addGestureRecognizer:tapGestureRecognizer];
        }
        
        - (void)closeKeyboardIfNeeded {
            if (![_uiSearchController.searchBar isFirstResponder]) {
                return;
            }
            [_uiSearchController.searchBar resignFirstResponder];
        }
        
        @end
        

        至于其他答案,请注意不断重新创建对象。无论是创建本身还是通过 ARC 进行的垃圾收集,总会有性能损失,这些事情会减慢您的主线程。根据您在主线程上所做的事情,它可能会对性能产生重大影响。

        【讨论】:

          猜你喜欢
          • 2014-06-20
          • 1970-01-01
          • 2014-03-06
          • 1970-01-01
          • 2013-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-08
          相关资源
          最近更新 更多