以编程方式呈现UISearchController

4

我正在以编程方式呈现一个UISearchController,而不将其添加到navigationItem中。日历应用程序执行类似操作。

如果没有呈现上下文,搜索栏会正确显示,但在推送另一个视图控制器后保留。

非呈现上下文

这是预期的,所以我们需要在列表视图控制器上设置definesPresentationContext... 但这会导致搜索栏呈现不正确。

有呈现上下文

这是设置呈现上下文的代码:

private lazy var searchController: UISearchController! = {
    let searchController = UISearchController(searchResultsController: nil)
    searchController.obscuresBackgroundDuringPresentation = false

    // If this is set to true, the search bar animates correctly, but that's
    // not the effect I'm after. See the next video.
    searchController.hidesNavigationBarDuringPresentation = false
    return searchController
}()

override func viewDidLoad() {
    super.viewDidLoad()
    definesPresentationContext = true
    searchButton.rx.tap.subscribe(onNext: { [unowned self] in
        present(searchController, animated: true)
    }).disposed(by: disposeBag)
}

使用hidesNavigationBarDuringPresentation可以解决问题,但我们会失去选项卡栏,并且整个界面看起来不太好。

仅使用hidesNavigationBarDuringPresentation

我尝试了此解决方案 (无法呈现UISearchController),但没有帮助。

有什么建议吗?

更新:问题更具体地说是,搜索栏出现在透明导航栏后面。将导航栏设置为不透明(navigationController?.navigationBar.isTranslucent = false)则搜索栏显示在导航栏下面。

3个回答

2
我也遇到了同样的问题,但是一直没有解决。似乎问题在于:

a)搜索控制器被呈现在视图控制器栈的最顶部,甚至高于导航控制器,因此它会保持活动状态进入下一个视图控制器推送。或者,

b)搜索控制器被呈现在导航控制器下面,因此它仍然被导航栏覆盖。

一种想法:不要将呈现搜索控制器的视图控制器嵌入导航控制器中。相反,只需创建一个看起来像顶部导航栏的UIView。这是否是不合适的解决方案?


嘿,Plato,感谢您的贡献!这似乎更像是应该作为评论发布的内容,供以后参考。 - Toadfish
@Toadfish 我同意!我本来想评论的,但是它说我需要至少50个声望才能评论 :/ - Plato
啊,是的。不幸的是,这经常发生。令人惊讶的是,这些年过去了他们从未找到更好的解决方案! - Toadfish

1
我还没有找到原问题的解决方案,但是我找到了一个解决方法:拦截导航事件,并手动关闭搜索控制器。
override func viewDidLoad() {
    ...
    // This makes the search bar appear behind the nav bar
    // definesPresentationContext = true

    navigationController?.delegate = self
}

extension JobListViewController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController,
                              willShow viewController: UIViewController, animated: Bool) {
        // `animated` is false because, for some reason, the dismissal animation doesn't start
        // until the transition has completed, when we've already arrived at the new controller
        searchController.dismiss(animated: false, completion: nil)
    }
}

1
我发现在导航栏上显示搜索控制器可以通过在导航控制器本身上调用present(_:animated:completion:)方法来实现,而不是在导航控制器的子视图上进行调用。
因此,在您的视图控制器中,您可以这样做:
navigationController?.present(searchController, animated: true)

这将像苹果日历应用程序中的搜索按钮一样运作。

enter image description here

更新

关于在将新控制器推送到导航堆栈之前关闭搜索控制器,您可以手动执行此操作,具体取决于如何进行推送。

以下所有操作都会在推送发生之前动画关闭搜索控制器。请注意,在关闭动画完成之前禁用用户交互以防止多次推送相同的视图控制器。

UIStoryboardSegue

将此重写添加到您的视图控制器中:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if navigationController?.presentedViewController == searchController {
        view.isUserInteractionEnabled = false
        searchController.dismiss(animated: true) {
            self.view.isUserInteractionEnabled = true
        }
    }
}

程序控制的IBAction

在将视图控制器推入导航堆栈之前,只需关闭搜索控制器:

@IBAction func showSecondTapped(_ sender: UIButton) {
    // Dismiss the search controller first.
    view.isUserInteractionEnabled = false
    searchController.dismiss(animated: true) {
        self.view.isUserInteractionEnabled = true
    }

    // Build and push the detail view controller.
    if let secondViewController = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") {
        navigationController?.pushViewController(secondViewController, animated: true)
    }
}

处理弹出手势

如果呈现搜索控制器的视图控制器不是导航控制器的根控制器,则用户可能能够使用交互式弹出手势,这将在弹出后仍保留搜索控制器的呈现。您可以通过使您的视图控制器成为搜索控制器的代理并遵循 UISearchControllerDelegate 协议,并添加以下代码来处理此问题:

extension ViewController: UISearchControllerDelegate {
    func willPresentSearchController(_ searchController: UISearchController) {
        navigationController?.interactivePopGestureRecognizer?.isEnabled = false
    }

    func willDismissSearchController(_ searchController: UISearchController) {
        navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    }
}

1
好的,我已经可以展示它了(请参见第一段屏幕录像)。但问题在于,当推送子控制器时,搜索栏仍然可见。而且在导航控制器上进行呈现对我也没有解决这个问题。 - alekop
@alekop 根据您的评论,我已更新了我的答案。我尝试使搜索控制器的解除转换和视图控制器的推送转换同时发生,但未能成功,因此我添加了代码以在解除转换正在进行时禁用用户交互。 - alobaili

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接