【问题标题】:SearchController issues in Navigation Bar导航栏中的 SearchController 问题
【发布时间】:2016-10-25 01:55:06
【问题描述】:

我正在尝试创建一个搜索栏,当您单击“搜索”(也在导航栏中)时,该搜索栏会显示在导航栏的顶部。单击搜索结果将带您进入搜索栏将消失的详细信息页面。当您单击返回 Home VC 时,搜索栏会重新出现。认为这很简单,但遇到了一些问题。这是我到目前为止所做的

要在 HomeVC 中点击“搜索”时创建搜索栏,我这样做了:

@IBAction func SearchAction(sender: AnyObject) {
        searchController.searchResultsUpdater = self
        searchController.searchBar.delegate = self
        definesPresentationContext = true
        searchController.dimsBackgroundDuringPresentation = false
        searchController.hidesNavigationBarDuringPresentation = false
        self.navigationController?.presentViewController(searchController, animated: true, completion: nil)
}

第一个问题,当你进入详情页面时,它的搜索栏并没有消失。

解决方案:在 PrepareForSegue 中,我添加了

self.searchController.searchBar.removeFromSuperview()

并将 unwindSegue 替换为委托,以便当用户从详细信息页面返回 Home VC 时,搜索栏会重新添加。 HomeVC 执行以下操作:

func didDismissDetail() {
    self.navigationController?.popViewControllerAnimated(true)
    if searchController.active == true {
        self.navigationController?.view.addSubview(searchController.searchBar)
    }
}

第 2 期:搜索的场景有一个 bug,点击进入详情页面,返回首页 VC,点击取消关闭搜索控制器。当您再次单击导航栏中的搜索按钮时,没有任何反应。我收到此错误:由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“应用程序试图以模态方式呈现活动控制器。” 解决方案:我添加了这个

func searchBarCancelButtonClicked(searchBar: UISearchBar) {
    self.searchController.searchBar.removeFromSuperview()        
}

这可行,但是当视图被移除时没有动画。抱歉,我知道这有点啰嗦 - 我的问题是:

  1. 有没有更好的方法来创造这种用户体验?
  2. 如果没有,当用户点击“取消”时,如何为搜索栏的移除设置动画

    这里有一些图片:

【问题讨论】:

    标签: ios swift search uinavigationbar uisearchcontroller


    【解决方案1】:

    对于问题 #1,您可以在 prepare(for:sender:) 中执行 self.searchController.isActive = false 而不是 self.searchController.searchBar.removeFromSuperview()。当您放松时,这将停用搜索栏。

    【讨论】:

      【解决方案2】:

      你应该使用 self.searchController.searchBar.removeFromSuperview() 而不是:

      navigationController?.dismissViewControllerAnimated(true, completion: nil)`
      

      关闭视图。另外,你应该给你的 segue 一个标识符并在prepareForSegue 中检查它。您的实现可能如下所示:

      override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
         if segue.identifier == "ToDetail" {
            let destination = segue.destinationViewController as! DetailViewController
            navigationController?.dismissViewControllerAnimated(true, completion: nil)
           //Pass Info to Detail View
         }
      }
      

      然后在didSelectRowAtIndexPath 中使用performSegueWithIdentifier 执行你的segue。

      override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
         performSegueWithIdentifier("ToDetail", sender: self)
      }
      

      要让 searchBar 重新出现,您可以在主视图控制器上设置一个名为 searchBarShouldBeOpen 的布尔变量。在您的SearchAction 函数中将此值设置为true,然后在viewWillAppear 中使用带有searchBarShouldBeOpen 的if 语句来决定是否运行SearchAction。此外,考虑将SearchAction 设为常规函数,并将除示例中显示的最后一行之外的所有内容移至viewDidLoad。最后,为您的主视图控制器实现UISearchBarDelegate 并覆盖searchBarCancelButtonClicked 并在其中将searchBarShouldBeOpen 设置为false。你的主视图控制器看起来像这样:

      class ViewController: UIViewController {
       //...Your Variables
      
       var searchBarShouldBeOpen: Bool = false
      
       let searchBarKey = "SearchBarText"
      
       override func viewDidLoad() {
          super.viewDidLoad()
          searchController.searchResultsUpdater = self
          searchController.dimsBackgroundDuringPresentation = false
          definesPresentationContext = true
          searchController.searchBar.delegate = self
          searchController.hidesNavigationBarDuringPresentation = false
      
          navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Search", style: .Plain, target: self, action: #selector(ViewController.showSearchBar))
        }
      
        override func viewWillAppear(animated: Bool) {
          super.viewWillAppear(animated)
          if searchBarShouldBeOpen {
             navigationController?.presentViewController(searchController, animated: false, completion: nil)
             searchBarShouldBeOpen = true
             if let previousText = NSUserDefaults.standardUserDefaults().stringForKey(searchBarKey) {
                searchController.searchBar.text = previousText
             }
          }  
        }
      
        func showSearchBar() {
          navigationController?.presentViewController(searchController, animated: true, completion: nil)
           searchBarShouldBeOpen = true
        }
      
        override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
          if segue.identifier == "ToDetail" {
            let destination = segue.destinationViewController as! DetailViewController
            if searchController.searchBar.text != "" { 
               NSUserDefaults.standardUserDefaults().setValue(searchController.searchBar.text, forKey: searchBarKey)
            }
             //Pass Info
          }
        }
        //Your functions...
      }
      
      extension ViewController: UISearchBarDelegate {
         func searchBarCancelButtonClicked(searchBar: UISearchBar) {
           searchBarShouldBeOpen = false
        }
      }
      

      在您的详细视图控制器中添加:

      override func viewWillAppear(animated: Bool) {
         super.viewWillAppear(animated)
         navigationController?.dismissViewControllerAnimated(true, completion: nil)
      }
      

      编辑:

      1. viewWillAppear 更改为手动呈现不带动画的UISearchBar,而不是使用showSearchBar,这样它就不会每次都动画了。

      2. 添加了对super.viewWillAppear(animated)的缺失调用

      3. UISearchBar 的解除移至详细视图控制器的 viewWillAppear,因此搜索不会在 segue 之前消失。

      4. 使用 NSUserDefaults(在 prepareForSegue 中)保留 UISearchBar 的文本,以便可以将其加载到主视图控制器的 viewWillAppear 中,以将搜索设置为以前的状态.

      5. 添加了searchBarKey 常量以用作UISearchBar 文本持久性的键,因此不会使用“魔术字符串”来设置和访问保存的文本。

      注意:您真的应该考虑使用didSelectRowAtIndexPath 来执行转场。

      其他选项:如果您希望 UISearchBar 在主视图控制器过渡到详细视图控制器的同时进行动画处理,您可以使用自定义 segue(这将使动画平滑一点)。我制作了一个自定义 segue 示例来创建这种效果。请注意,您必须将 Interface Builder 中的视图控制器之间的 segue 更改为具有“自定义”类型并成为 UIStoryboardSegue 的以下子类。

      class CustomSegue: UIStoryboardSegue {
      
         override func perform() {
            let pushOperation = NSBlockOperation()
            pushOperation.addExecutionBlock {
               self.sourceViewController.navigationController?.pushViewController(self.destinationViewController, animated: true)
            }
            pushOperation.addExecutionBlock {
              self.sourceViewController.navigationController?.dismissViewControllerAnimated(true, completion: nil)
            }
            pushOperation.queuePriority = .Normal
            pushOperation.qualityOfService = .UserInitiated
            let operationQueue = NSOperationQueue.mainQueue()
            operationQueue.addOperation(pushOperation)
      
         }
      
      }
      

      您可以在以下链接中阅读有关自定义 segue 中代码的更多信息:

      1. 自定义转场:http://www.appcoda.com/custom-segue-animations/
      2. NSOperation:http://nshipster.com/nsoperation/

      希望这会有所帮助!如果您需要详细说明,请询问! :)

      【讨论】:

      • 感谢 Ike10 的回复。我试过了,但关闭的问题是它停用了搜索控制器,所以当你从 Detail 返回 Home VC 时,搜索栏不会再次出现。如果在 Home VC 登陆时将当前 VC 添加到委托方法,则无法区分搜索控制器是否事先处于活动状态
      • 太棒了@Ike10!是的,这会奏效。我已经实现了这个并遇到了另外两个小问题:1)当您从 HomeVC 转到 Detail 时,您会看到它在进入 Detail 之前从搜索结果返回到原始列表。仅供参考,我没有使用 didSelectRowAtIndexPath。我在 cellForRowAtIndexPath 中使用了 'let indexPath = tableView.indexPathForSelectedRow'。应该是一样的
      • 2) 当您从 Detail 返回搜索结果时,它总是会显示动画。您应该只看到搜索结果(您看到的最后一页)。我创建了一个带有动画的第二个方法来关闭并要求 viewWillAppear 如果 searchBarShouldBeOpen = true 来调用它。这不起作用,不知道为什么。
      • @Danny 查看我的编辑内容,了解您的问题的可能解决方案。
      • @Danny 为从 HomeVC 到 DetailVC 的更清晰过渡添加了一些内容
      猜你喜欢
      • 2016-06-14
      • 1970-01-01
      • 2021-06-04
      • 1970-01-01
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多