繁体   English   中英

无法将 searchBar 设置为 firstResponder

Cannot set searchBar as firstResponder

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

我在 tableviewcontroller 中设置了一个 searchBar。 我已经引用了这个类似的问题UISearchBar 在 UITableView 重新出现后无法成为第一响应者,但我仍然无法将其设置为第一响应者。 在 .h 文件中:

@property (strong, nonatomic) UISearchController *searchController;
@property (strong, nonatomic) IBOutlet UISearchBar *searchBar;

在视图didload中:

self.searchController = [[UISearchController alloc]initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;

self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.tableView.tableHeaderView = self.searchController.searchBar;

在 viewDidAppear 中:

-(void)viewDidAppear:(BOOL)animated {
  [self.searchController setActive:YES];
  [self.searchController.searchBar becomeFirstResponder];
  [super viewDidAppear:animated];
}

当我转到视图时,searchBar 会进行动画处理,但没有出现键盘。

22 个回复

我也注意到了这个问题。 似乎发生的是当 searchController 仍在“加载”时调用becomeFirstResponder就完成了。 如果您注释掉becomeFirstResponder您会注意到没有区别。 所以我们需要一种在 searchController 加载完成后调用 becomeFirstResponder 的方法。

当我查看各种委托方法时,我注意到有一个委托方法:

- (void)didPresentSearchController:(UISearchController *)searchController

这个方法在 searchController 出现后立即调用。 然后我打电话给becomeFirstResponder

- (void)didPresentSearchController:(UISearchController *)searchController
{
    [searchController.searchBar becomeFirstResponder];
}

这解决了问题。 您会注意到,当 searchController 加载时,搜索栏现在具有焦点。

使用 - (void)didPresentSearchController:(UISearchController *)searchController的解决方案不起作用,因为仅当用户点击搜索栏时才会调用此委托方法...

但是,此解决方案确实有效:

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.1];
}

- (void) showKeyboard
{
    [self.searchController.searchBar becomeFirstResponder];
}

斯威夫特 3

delay(0.1) { self.searchController.searchBar.becomeFirstResponder() }

func delay(_ delay: Double, closure: @escaping ()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

好吧,我找到了实际上对我来说非常有效的解决方案。

不要调用[self.searchController setActive:YES]; 在调用[self.searchController.searchBar becomeFirstResponder];

更好的是,不要调用[self.searchController setActive:YES]; 根本。

只调用[self.searchController.searchBar becomeFirstResponder]; 键盘会按原样弹出,没有任何延迟。

这似乎有点像一个错误,很多人都在确认它。 例如,在这里检查: 通过becomeFirstResponder 将焦点分配给UISearchController 的UISearchBar 时,键盘不会出现

斯威夫特 4,iOS 11

这个对我有用

// 1.
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    resultSearchController.isActive = true
}

// 2. ->> UISearchControllerDelegate
func didPresentSearchController(_ searchController: UISearchController) {

    DispatchQueue.main.async {
        searchController.searchBar.becomeFirstResponder()
    }
}

函数searchController.searchBar.becomeFirstResponder()必须在主线程中调用,并且在 viewDidLoad 方法中searchController.active = true之后searchController.active = true 这是完整的解决方案。 它适用于 iOS 9.3

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
  searchController.active = true
  Async.main {
    self.searchController.searchBar.becomeFirstResponder()
  }
}

与其他答案非常相似,但我必须访问ViewDidAppear的主队列 SearchBarControllerView出现之前不能被操作,然后只能在UI的主队列中执行:

searchController.active = true  // doubtful whether this is needed

        dispatch_async(dispatch_get_main_queue(), {
            self.searchController.searchBar.becomeFirstResponder()
        });

简单的Swift3变体:

override func viewDidLoad() {
    super.viewDidLoad()
    navigationItem.titleView = mySearchController.searchBar
    mySearchController.searchResultsUpdater = self
    mySearchController.delegate = self

}

override func viewDidAppear(_ animated: Bool) {
    DispatchQueue.main.async {
        self.mySearchController.isActive = true
    }
}

func presentSearchController(_ searchController: UISearchController) {
    mySearchController.searchBar.becomeFirstResponder()
}

有用 ;-)

Swift 5解决方案:

override func viewDidLoad() {
        super.viewDidLoad()
        definesPresentationContext = true
        searchController.delegate = self
    }

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
}

extension ViewController: UISearchControllerDelegate {
 func didPresentSearchController(_ searchController: UISearchController) {
      DispatchQueue.main.async {[weak self] in
          self?.searchController.searchBar.becomeFirstResponder()
       }
  }
}

Xcode 11.4,Swift 5.2

如果您只想显示SearchBar但不激活TextField和键盘。 无需将其分派到主线程。

在此处输入图片说明

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
}

如果您希望通过键盘激活TextField ,那么您确实需要在主线程上调用它。 无需激活SearchController ,这会自动发生。

在此处输入图片说明

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    DispatchQueue.main.async {
        self.searchController.searchBar.becomeFirstResponder()
    }
}

这可能取决于您如何配置SearchController 这是我配置我的方式:

// Defined in class
let searchController = UISearchController(searchResultsController: nil)

// Called in viewDidLoad
navigationItem.searchController = searchController
searchController.searchResultsUpdater = self
searchController.searchBar.scopeButtonTitles = ["A", "B"]
searchController.searchBar.delegate = self          
searchController.obscuresBackgroundDuringPresentation = false

替代方法

此答案着眼于与此事件相关的许多问题,并解释了在我的案例中用于隔离和确定问题的具体原因的调试逻辑。 一路上,我分享了在其他情况下可能有效的其他可能性,并解释了为什么这在我的情况下有效。


tldr ; 查看 self.definesPresentationContext = true、isBeingDismissed、isBeingPresented、canBecomeFirstResponder 和 SearchController、Searchbar、SearchResultsUpdater 及其委托方法的委托分配。

self.definesPresentationContext解决方案的来源- 请参阅 SO 答案

要记住的一件事是 SearchBar 呈现方式的上下文。 嵌入在工具栏、导航栏、另一个 UIView 中,作为输入或输入附件视图。 我发现所有这些都会对搜索栏显示或关闭时的时间和内部动画产生一些影响。

我已经尝试了所有提出的解决方案,但在我重新考虑如何尝试使用 searchBar 之前,这些解决方案都没有奏效。 就我而言,我正在推送一个控制器(B)和一个来自控制器(A)的搜索控制器,该控制器(A)上已经有一个初始搜索控制器。 在进行拉刷新时,我以编程方式将每个搜索控制器嵌入到导航项的 titleView 中。

建议将searchbar.becomeFirstResponder()添加到生命周期中的答案对我的用例没有意义,因为在我想将我的搜索栏插入并显示到导航项时视图已完全加载。 逻辑似乎也很混乱,因为视图控制器生命周期方法应该已经在主线程上运行了。 在演示中添加延迟似乎也干扰了系统使用的显示和动画的内部操作。

我发现当我从控制器 A 推送视图控制器时调用我的函数来切换插入工作,但从控制器 B 推送时键盘无法正确显示。 这两种情况的区别在于 controllerA 是一个静态 tableview 控制器,controllerB 有一个 tableview 控制器,我通过添加自己的搜索控制器使其可搜索。

例如controllerA与搜索栏塞格斯到controllerB与搜索栏

使用多个断点并检查 searchController 和 searchbar 在不同点的状态,我能够确定searchController.canBecomeFirstResponder正在返回false 我还发现我需要将SearchResultsUpdater设置为self以及 searchController 和 searchBar 上的委托。

我终于注意到,当我将 controllerB 推到导航堆栈上时,在 controllerA 上设置self.definesPresentationContext = true不允许显示键盘。 我的解决方案是将self.definesPresentationContext = true移动到self.definesPresentationContext = true上的viewDidAppear并在 controllerA 的prepare(for:sender:)方法中,当目标是 controllerB 时,我将其更改为self.definesPresentationContext = false 这解决了我的键盘显示问题。

关于动画的一句话~我发现在将东西分配给导航项或导航栏时,系统有一些内置的计时和默认动画。 我避免添加自定义动画、moveToParent 方法中的代码或延迟演示,因为在许多情况下会发生意外行为。

为什么这个解决方案有效

关于definesPresentationContext Apple 文档指出了默认行为,并指出了一些情况,在这些情况下,此上下文会调整控制器分配来管理键盘外观的行为。 在我的例子中,控制器 A 被分配来管理演示而不是控制器 B,所以我只是通过调整这个值来改变这种行为:

当使用 currentContext 或 overCurrentContext 样式来呈现视图控制器时,此属性控制视图控制器层次结构中的哪个现有视图控制器实际上被新内容覆盖。 当基于上下文的呈现发生时,UIKit 从呈现的视图控制器开始并沿着视图控制器层次结构向上走。 如果它找到一个视图控制器的这个属性的值为真,它会要求该视图控制器呈现新的视图控制器。 如果没有视图控制器定义表示上下文,UIKit 会要求窗口的根视图控制器来处理表示。 此属性的默认值为 false。 一些系统提供的视图控制器,例如 UINavigationController,将默认值更改为 true。

答案是在 viewDidAppear 中调用 becomeFirstResponder。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchBar?.becomeFirstResponder()
}

这就是它在 Swift 4 中对我有用的东西

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
    DispatchQueue.main.async{
        self.searchController.searchBar.becomeFirstResponder()
    }
}

基于@mislovr 的解决方案, 0.1延迟还不够长。 这是我对该答案的更新代码。

func presentSearchController() {
    searchController.isActive = true

    Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak searchController] timer in
        guard let searchController = searchController else {
            timer.invalidate()
            return
        }

        if searchController.searchBar.canBecomeFirstResponder {
            searchController.searchBar.becomeFirstResponder()
            timer.invalidate()
        }
    }
}

我解决了这个问题:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
    
}

func didPresentSearchController(_ searchController: UISearchController) {

    DispatchQueue.main.async {
        searchController.searchBar.searchTextField.becomeFirstResponder()
    }
    
}

发生的事情是,一旦视图出现,您就将 searchControllers 的“isActive”属性设置为 true。 这会触发名为 didPresentSearchController 的委托方法。 当它触发时,意味着 searchcontroller 是可见的,因此可以成为第一响应者。

我认为可能有更清洁的解决方案。 我发现键盘在出现时有点“闪烁”然后又下降,并且在didPresentSearchController:调用becomeFirstResponder正在工作,但是键盘来晚了,动画有点古怪。

用演示检查包装我的重新加载数据方法让每个人都高兴:

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
    if (!searchController.isBeingPresented && !searchController.isBeingDismissed) {
        [self.collectionView reloadData];
    }
}

我通过在resignFirstResponder设置断点发现了这resignFirstResponder resignFirstResponder是从reloadData调用的,而reloadData又被updateSearchResultsForSearchController:调用。 结果是updateSearchResultsForSearchController:在搜索控制器的呈现期间被调用。 如果您在此期间处理 UISearchBar 所在的视图层次结构,则搜索控制器会变得无聊。 我的猜测是 reloadData 调用导致UICollectionReusableView标头视图出现并返回到视图层次结构并迫使 UISearchBar 子视图退出第一响应者。

我看到的另一个症状是搜索词在取消时没有重置到搜索栏的中间,这导致它在以后的点击中无法正确显示。

混合 @edwardmp 和 @mislovr 答案的解决方案对我有用(键盘弹出略有延迟):

- (void)didPresentSearchController:(UISearchController *)searchController {
    [self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.001];
}

- (void) showKeyboard {
    [self.searchController.searchBar becomeFirstResponder];
}

我与此斗争了一段时间,但通过以下方式使其工作:

  • viewDidLoad()初始化searchController
  • viewDidAppear()设置active = true
    • 这会触发UISearchControllerDelegate扩展中的didPresentSearchController()
  • 设置searchBar.becomeFirstResponder()didPresentSearchController()

这是完整的示例,它使用 Google Maps Autocomplete。

class myViewController: UIViewController {

    // MARK: Variables

    var resultsViewController: GMSAutocompleteResultsViewController?
    var searchController: UISearchController?
    var resultView: UITextView?

    // MARK: Outlets

    @IBOutlet var myView: UIView!

    // MARK: View Methods

    override func viewDidLoad() {
        super.viewDidLoad()

        resultsViewController = GMSAutocompleteResultsViewController()
        resultsViewController?.delegate = self

        searchController = UISearchController(searchResultsController: resultsViewController)
        searchController?.delegate = self
        searchController?.searchResultsUpdater = resultsViewController

        searchController?.searchBar.prompt = "Search for a Place"
        searchController?.searchBar.placeholder = "place name"
        searchController?.searchBar.text = ""
        searchController?.searchBar.sizeToFit()

        searchController?.searchBar.returnKeyType = .Next
        searchController?.searchBar.setShowsCancelButton(true, animated: false)

        myView.addSubview((searchController?.searchBar)!)
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(true)
        searchController?.active = true
    }

    // MARK: GMSAutocompleteResultsViewControllerDelegate Extension

    extension myViewController: GMSAutocompleteResultsViewControllerDelegate {

        func resultsController(resultsController: GMSAutocompleteResultsViewController,
                       didAutocompleteWithPlace place: GMSPlace) {
            searchController?.active = false
            // Do something with the selected place.
            print("Place name: ", place.name)
            print("Place address: ", place.formattedAddress)
            print("Place attributions: ", place.attributions)
        }

        func resultsController(resultsController: GMSAutocompleteResultsViewController,
                       didFailAutocompleteWithError error: NSError){
            // TODO: handle the error.
            print("Error: ", error.description)
        }

        // Turn the network activity indicator on and off again.
        func didRequestAutocompletePredictionsForResultsController(resultsController: GMSAutocompleteResultsViewController) {
            UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        }

        func didUpdateAutocompletePredictionsForResultsController(resultsController: GMSAutocompleteResultsViewController) {
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
        }
    }

    extension myViewController: UISearchControllerDelegate {

        func didPresentSearchController(searchController: UISearchController) {
            self.searchController?.searchBar.becomeFirstResponder()
        }
    }
}

在目标 C 中:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Setup your SearchViewController here...
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // Will show Cancel button in White colour 
    [[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTintColor:[UIColor whiteColor]];

    // searchController.active = YES;   // This is not necessary

    // set SearchBar first responder inside Main Queue block
    dispatch_async(dispatch_get_main_queue(), ^{
        [self->searchController.searchBar becomeFirstResponder];
    });
}

斯威夫特 5

class ViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // Only after viewDidAppear searchController can be activated
        searchController?.isActive = true
    }
}

extension ViewController: UISearchControllerDelegate {
    // didPresentSearchController not work for me
    func presentSearchController(_ searchController: UISearchController) {
        searchController.searchBar.becomeFirstResponder()
    }
}

对我来说,使用viewDidAppear有很大的滞后。 viewDidLoad异步使用becomeFirstResponder会更好(使用 iOS 10、Swift 3 测试):

override func viewDidLoad() {
    super.viewDidLoad()

    DispatchQueue.main.async {
        searchController.searchBar.becomeFirstResponder()
    }
}

这就是它在 Swift 3 中对我有用的东西。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.perform(#selector(self.showKeyboard), with: nil, afterDelay: 0.1)
}

func showKeyboard() {
    self.searchController.searchBar.becomeFirstResponder()
}
  1. 定义搜索栏。
  2. SearchBar 文本字段“searchField”选择。
  3. viewDidLoad 或 viewWillAppear 调用代码。

@IBOutlet 弱 var searchBar:UISearchBar!

让 textFieldUISearchBar = searchBar.value(forKey: "searchField") as? 用户界面文本字段

textFieldUISearchBar?.becomeFirstResponder()

1 无法在searchBar上调用成为FirstResponder

我正在尝试这样做,以便当用户转到搜索页面时,键盘立即可用。 我正在使用此代码(在块运行后会在viewDidLoad中调用它创建了resultSearchController): 做这个的最好方式是什么? ...

2015-12-29 07:03:09 1 64   ios/ swift
2 `[searchbar成为FirstResponder]`内存重吗?

在跟踪内存使用情况时,我注意到[searchbar becomeFirstResponder]; 最多使用4mb的内存。 现在我问为什么? 那是正常的吗? 我对此有些困惑。 当注释该行代码时,我的应用程序的内存使用量是-3-4 mb。 ...

5 firstResponder未设置[重复]

该问题与以下内容完全相同: 未正确设置第一响应者 2个答案 编辑完UITextField并按回车后,我循环浏览并检查下一个字段。 我得到了该字段的索引路径,然后将其保存。 接下来,我重新加载表数据,并在构建表时,如果indexPath等于下一个文本字段的inde ...

7 什么是我的FirstResponder

我创建了一个应用程序,在ViewController.m , viewDidLoad ,我编写了以下代码 但它会为一切返回NO 。 默认情况下,我的第一响应者是什么? ...

9 如何使SearchBar像iPhone设置

嗨,谢谢。提前回答,我有2个问题。第一个问题是如何使搜索栏像设置一样在没有搜索任何内容的情况下显示,但背景会显示该设置,其次如何从静态tableview中显示搜索结果 ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM