【问题标题】:UISearchBar disable auto disable of cancel buttonUISearchBar 禁用取消按钮的自动禁用
【发布时间】:2011-05-19 21:36:38
【问题描述】:

我已经在表格视图中实现了一个 UISearchBar,除了一件小事外,几乎所有东西都在工作:当我输入文本然后按下键盘上的搜索按钮时,键盘消失了,搜索结果是显示的唯一项目在表格中,文本保留在 UISearchBar 中,但取消按钮被禁用。

我一直试图让我的列表尽可能接近 Apple 联系人应用程序的功能,当您在该应用程序中按搜索时,它不会禁用取消按钮。

当我查看 UISearchBar 头文件时,我注意到 _searchBarFlags 结构下的 autoDisableCancelButton 标志,但它是私有的。

我在设置 UISearchBar 时是否遗漏了什么?

【问题讨论】:

    标签: ios4 uisearchbar cancel-button


    【解决方案1】:

    我找到了解决方案。您可以使用此 for 循环循环搜索栏的子视图,并在按下键盘上的搜索按钮时启用它。

    for (UIView *possibleButton in searchBar.subviews)
    {
        if ([possibleButton isKindOfClass:[UIButton class]])
        {
            UIButton *cancelButton = (UIButton*)possibleButton;
            cancelButton.enabled = YES;
            break;
        }
    }
    

    【讨论】:

    • 这在 iOS 6 上对我不起作用,因为唯一存在的按钮是 UINavigationButton,它似乎是一个私有类。
    • 你试过在 UISearchBar 上设置 showCancelButton 属性吗?
    • UINavigationButton 是 UIButton 的子类,所以这段代码有效,并且没有私有类的使用。 github.com/kennytm/iphone-private-frameworks/blob/master/UIKit/…
    • 人们只需要在适当的时候使用它,也就是在搜索栏失去焦点之后(resignFirstResponder)。
    • 为我工作!我做了((UIButton*)possibleButton).enabled = YES;,因为我发现cancelButton 的使用太多了。
    【解决方案2】:

    我必须稍微调整一下才能让它在 iOS7 中为我工作

    - (void)enableCancelButton:(UISearchBar *)searchBar
    {
        for (UIView *view in searchBar.subviews)
        {
            for (id subview in view.subviews)
            {
                if ( [subview isKindOfClass:[UIButton class]] )
                {
                    [subview setEnabled:YES];
                    NSLog(@"enableCancelButton");
                    return;
                }
            }
        }
    }
    

    【讨论】:

    • 你真的应该退出内部 if 语句而不是中断。该 break 语句只会让您跳出内部 for 循环,即使在找到取消按钮后也会继续循环 searchBar.subviews。
    【解决方案3】:

    有两种方法可以轻松实现这一点

    - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
        //  The small and dirty
        [(UIButton*)[searchBar valueForKey:@"_cancelButton"] setEnabled:YES];
    
        // The long and safe
         UIButton *cancelButton = [searchBar valueForKey:@"_cancelButton"];
        if ([cancelButton respondsToSelector:@selector(setEnabled:)]) {
             cancelButton.enabled = YES;
        }
    }
    

    您应该使用第二个,如果 Apple 在后台更改它,它不会使您的应用程序崩溃。

    顺便说一句,我从 iOS 4.0 到 8.2 对其进行了测试,没有任何变化,我也在我的商店批准的应用程序中使用它,没有任何问题。

    【讨论】:

    • 这可行,但您的应用可能会被拒绝,因为它使用的是私有方法。当心。
    • @PedroJoséCardoso 不,这并不能真正算作私人 api 使用,我从未看到任何因此而被拒绝的情况。
    【解决方案4】:

    这就是我在 iOS 6 上工作的原因:

    searchBar.showsCancelButton = YES;
    searchBar.showsScopeBar = YES;
    [searchBar sizeToFit];
    [searchBar setShowsCancelButton:YES animated:YES];
    

    【讨论】:

    • 这并不能解决问题:op 想要禁用自动禁用功能,即不允许 UISearchBar 禁用它。
    【解决方案5】:

    这是我的解决方案,适用于所有 iOS 版本的所有情况。

    IE,当键盘因为用户拖动滚动视图而被解除时,其他解决方案无法处理。

    - (void)enableCancelButton:(UIView *)view {
        if ([view isKindOfClass:[UIButton class]]) {
            [(UIButton *)view setEnabled:YES];
        } else {
            for (UIView *subview in view.subviews) {
                [self enableCancelButton:subview];
            }
        }
    }
    
    // This will handle whenever the text field is resigned non-programatically
    // (IE, because it's set to resign when the scroll view is dragged in your storyboard.)
    - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
        [self performSelector:@selector(enableCancelButton:) withObject:searchBar afterDelay:0.001];
    }
    
    // Also follow up every [searchBar resignFirstResponder];
    // with [self enableCancelButton:searchBar];
    

    【讨论】:

      【解决方案6】:

      根据my answer here,将其放在您的 searchBar 委托中:

      - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
      {   
          dispatch_async(dispatch_get_main_queue(), ^{
              __block __weak void (^weakEnsureCancelButtonRemainsEnabled)(UIView *);
              void (^ensureCancelButtonRemainsEnabled)(UIView *);
              weakEnsureCancelButtonRemainsEnabled = ensureCancelButtonRemainsEnabled = ^(UIView *view) {
                  for (UIView *subview in view.subviews) {
                      if ([subview isKindOfClass:[UIControl class]]) {
                          [(UIControl *)subview setEnabled:YES];
                      }
                      weakEnsureCancelButtonRemainsEnabled(subview);
                  }
              };
      
              ensureCancelButtonRemainsEnabled(searchBar);
          });
      }
      

      【讨论】:

        【解决方案7】:

        没有一个答案对我有用。我的目标是 iOS 7。但我找到了答案。

        我正在尝试类似 Twitter iOS 应用程序。如果您单击“时间线”选项卡中的放大镜,则会出现 UISearchBar 并激活“取消”按钮、显示键盘和最近的搜索屏幕。滚动最近的搜索屏幕,它会隐藏键盘,但会保持“取消”按钮处于激活状态。

        这是我的工作代码:

        UIView *searchBarSubview = self.searchBar.subviews[0];
        NSArray *subviewCache = [searchBarSubview valueForKeyPath:@"subviewCache"];
        if ([subviewCache[2] respondsToSelector:@selector(setEnabled:)]) {
            [subviewCache[2] setValue:@YES forKeyPath:@"enabled"];
        }
        

        我通过在表视图的scrollViewWillBeginDragging: 处设置断点来实现此解决方案。我查看了我的UISearchBar 并展示了它的子视图。它总是只有一个,类型为UIView(我的变量searchBarSubview)。

        然后,UIView 拥有一个名为subviewCacheNSArray,我注意到最后一个元素,即第三个元素,是UINavigationButton 类型,不在公共API 中。所以我开始改用键值编码。我检查了UINavigationButton 是否响应setEnabled:,幸运的是,确实如此。所以我将属性设置为@YES。原来UINavigationButton 取消按钮。

        如果 Apple 决定更改 UISearchBar 的内部结构的实现,这势必会被打破,但到底是什么。它现在有效。

        【讨论】:

          【解决方案8】:

          这是一个 Swift 3 解决方案,它利用扩展轻松获取取消按钮:

          extension UISearchBar {
              var cancelButton: UIButton? {
                  for subView1 in subviews {
                      for subView2 in subView1.subviews {
                          if let cancelButton = subView2 as? UIButton {
                              return cancelButton
                          }
                      }
                  }
                  return nil
              }
          }
          

          现在使用:

          class MyTableViewController : UITableViewController, UISearchBarDelegate {
          
              var searchBar = UISearchBar()
          
              func viewDidLoad() {
                  super.viewDidLoad()
                  searchBar.delegate = self
                  tableView.tableHeaderView = searchBar
              }
          
              func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
                  DispatchQueue.main.async {
                      if let cancelButton = searchBar.cancelButton {
                          cancelButton.isEnabled = true
                          cancelButton.isUserInteractionEnabled = true
                      }
                  }
              }
          }
          

          【讨论】:

          • 我见过的最干净的解决方案。谢谢。
          • 不适用于 iOS 13+,因为 subView2 不是 UIButton
          【解决方案9】:

          对于 Monotouch 或 Xamarin iOS,我有以下适用于 iOS 7 和 iOS 8 的 C# 解决方案:

          foreach(UIView view in searchBar.Subviews)
          {
              foreach(var subview in view.Subviews)
              {
                  //Console.WriteLine(subview.GetType());
                  if(subview.GetType() == typeof(UIButton))
                  {
                      if(subview.RespondsToSelector(new Selector("setEnabled:")))
                      {
                          UIButton cancelButton = (UIButton)subview;
                          cancelButton.Enabled = true;
                          Console.WriteLine("enabledCancelButton");
                          return;
                      }
                  }
              }
          }
          

          此答案基于David Douglas 解决方案。

          【讨论】:

            【解决方案10】:

            更完整的答案:

            • 从 iOS 7 开始,在 searchBar 下多了一层子视图
            • 启用取消按钮的好地方是searchBarTextDidEndEditing

            .

            extension MyController: UISearchBarDelegate {
              public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
                DispatchQueue.main.async {
                // you need that since the disabling will
                // happen after searchBarTextDidEndEditing is called
                  searchBar.subviews.forEach({ view in
                    view.subviews.forEach({ subview in
                      // ios 7+
                      if let cancelButton = subview as? UIButton {
                        cancelButton.isEnabled = true
                        cancelButton.isUserInteractionEnabled = true
                        return
                      }
                    })
                    // ios 7-
                    if let cancelButton = subview as? UIButton {
                      cancelButton.isEnabled = true
                      cancelButton.isUserInteractionEnabled = true
                      return
                    }
                  })
                }
              }
            }
            

            【讨论】:

              【解决方案11】:

              时间过去了,但问题依然存在……

              优雅的Swift 5/iOS 13解决方案:

              func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
                  for case let cancelButton as UIButton in searchBar.subviews {
                      cancelButton.isEnabled = true
                  }
              }
              

              【讨论】:

              • 在 iOS 13 中 UISearchBar.subviews 只返回一个元素,而不是 UIButton
              猜你喜欢
              • 2017-03-25
              • 2016-07-09
              • 1970-01-01
              • 1970-01-01
              • 2012-02-28
              • 1970-01-01
              • 1970-01-01
              • 2020-05-26
              • 1970-01-01
              相关资源
              最近更新 更多