导航栏中的 SearchController 问题

SearchController issues in Navigation Bar

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

在主屏幕中点击 "Search" 时创建搜索栏VC 我这样做了:

@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 替换为委托,以便当用户从详细信息页面返回主页 VC 时,搜索栏会重新添加。主页VC 执行以下操作:

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

第二期:搜索场景有bug,点击进入详情页面,返回首页VC,点击取消关闭搜索控制器。当您再次单击导航栏中的搜索按钮时,没有任何反应。我收到此错误:由于未捕获的异常 'NSInvalidArgumentException',正在终止应用程序,原因:'Application tried to present modally an active controller .' 解决方案:我添加了这个

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

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

  1. 有没有更好的方法来打造这种用户体验?
  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. 的缺失调用
  4. UISearchBar 的解除移动到详细视图控制器的 viewWillAppear,这样搜索就不会在继续之前消失。

  5. 使用 NSUserDefaults(在 prepareForSegue 中)保留了 UISearchBar 的文本,这样它就可以加载到 viewWillAppear 中主视图控制器将搜索设置为之前的内容。

  6. 添加了 searchBarKey 常量用作 UISearchBar 文本持久性的键,因此 "Magic String" 不会用于设置和访问保存的文字.

注:你真的应该考虑使用didSelectRowAtIndexPath来执行segue。

其他选项:如果您希望UISearchBar在主视图控制器到详细视图控制器的转换同时进行动画处理,您可以使用自定义segue(这将使动画更流畅)。我制作了一个创建此效果的自定义 segue 示例。请注意,您必须将 Interface Builder 中的视图控制器之间的 segue 更改为 "Custom" 类型并成为 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)

   }

}

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

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

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

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