无法将searchBar设置为firstResponder

use*_*663 44 objective-c uisearchbar first-responder ios uisearchcontroller

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

@property (strong, nonatomic) UISearchController *searchController;
@property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
Run Code Online (Sandbox Code Playgroud)

在视图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;
Run Code Online (Sandbox Code Playgroud)

并在viewDidAppear:

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

当我转向视图时,searchBar会动画,但不会出现键盘.

edw*_*dmp 57

我也注意到了这个问题.似乎发生的是becomeFirstResponder当searchController仍在"加载"时调用.如果你发表评论,becomeFirstResponder你会注意到没有区别.所以我们需要一种在searchController'完成'加载后调用becomeFirstResponder的方法.

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

- (void)didPresentSearchController:(UISearchController *)searchController
Run Code Online (Sandbox Code Playgroud)

在呈现searchController之后立即调用此方法.然后我打电话给becomeFirstResponder:

- (void)didPresentSearchController:(UISearchController *)searchController
{
    [searchController.searchBar becomeFirstResponder];
}
Run Code Online (Sandbox Code Playgroud)

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

  • @Besi我想我知道你做错了什么; 你需要打开/激活searchViewController,例如将它放在你的viedDidLoad中:`[self.searchController setActive:TRUE];` (8认同)
  • 这在iOS8中对我不起作用.只有当我手动触摸SearchBar的文本字段时,才会调用`didPresentSearchController`. (7认同)
  • @Besi确保也将becomeFirstResponder调用抛出到主线程上.出于某种原因,在后台线程的某些iOS版本中调用了didPresentSearchController! (5认同)
  • iOS 9.3这不起作用.不幸的是,mislovr的答案或者dispatch_after是唯一的解决方案. (5认同)
  • [self.searchController setActive:TRUE]; 必须放入viewDidAppear,如果放在viewDidLoad中,它将无法正常工作. (3认同)

小智 42

带有 - 的解决方案(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];
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特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)
}
Run Code Online (Sandbox Code Playgroud)

  • 或者更简单: [self.searchController.searchBar performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.1]; (2认同)

Rol*_* T. 18

好吧,我发现解决方案实际上对我来说非常有用.

打电话[self.searchController setActive:YES];前不要打电话[self.searchController.searchBar becomeFirstResponder];

什么更好,不要打电话[self.searchController setActive:YES];.

只打电话[self.searchController.searchBar becomeFirstResponder];,键盘弹出应该没有任何延迟.

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

  • 它不适合我.你在哪里调用`[self.searchController.searchBar becomeFirstResponder];`? (4认同)

Tan*_* Vu 10

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

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

  • 设置`searchController.active`首先为我工作.为什么在视图生命周期方法中需要`Async.main`调用,这不保证在主线程上运行吗? (2认同)
  • UISearchController目前似乎还有一些未完成的工作(好像他们过早地调用了他们的委托方法),并且Async.main只是在未来的迭代中调度becomeFirstResponder(),绕过该bug. (2认同)

Vla*_*iak 9

Swift 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()
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 8

非常相似,其他的答案,但我不得不进入主队列ViewDidAppear.在SearchBarController无法采取行动,直到View出现,然后只能在主队列这样做UI:

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

        dispatch_async(dispatch_get_main_queue(), {
            self.searchController.searchBar.becomeFirstResponder()
        });
Run Code Online (Sandbox Code Playgroud)

  • 始终在主队列上调用viewDidAppear.这段代码实际上是在主队列的未来迭代中调度becomeFirstResponder(),允许UISearchController完成此时尚未完成的任何操作. (4认同)
  • 不设置`active`变量使它对我有用. (2认同)

Vit*_*pov 6

Easy 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()
}
Run Code Online (Sandbox Code Playgroud)

有用 ;-)


UnR*_*ewa 6

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()
       }
  }
}
Run Code Online (Sandbox Code Playgroud)


Tom*_* C. 5

替代方法

\n\n
\n

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

\n
\n\n
\n\n
\n

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

\n
\n\n

(解决方案来源self.definesPresentationContext-参见SO答案

\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n
\n

例如,带有搜索栏的控制器 A转至带有搜索栏的控制器 B

\n
\n\n

使用多个断点并在不同点检查 searchController 和搜索栏的状态,我能够确定正在searchController.canBecomeFirstResponder返回false. 我还发现我需要在 searchController 和 searchBar 上设置SearchResultsUpdaterto和 delegate。self

\n\n

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

\n\n

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

\n\n

为什么这个解决方案有效

\n\n

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

\n\n
\n

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

\n
\n


rba*_*win 5

Xcode 11.4,Swift 5.2

如果您只想SearchBar出现而不激活TextField& 键盘。无需将其分派到主线程。

在此处输入图片说明

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
}
Run Code Online (Sandbox Code Playgroud)

如果你想TextField用键盘激活,那么你需要在主线程上调用它。无需SearchController激活,这会自动发生。

在此处输入图片说明

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

这可能取决于您如何配置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
Run Code Online (Sandbox Code Playgroud)