UISearchController 的自定义动画?
Custom animation for UISearchController?
我正在展示 UISearchController
来自嵌入导航控制器的控制器。出现默认动画,其中搜索框从导航栏顶部下拉。
这对我来说不是一个好的用户体验,因为我会在用户点击屏幕中间的 UITextField
时显示搜索。我想做的是让 UITextField
浮动到顶部并变成搜索框,但我不知道该怎么做。
这是我的:
class PlacesSearchController: UISearchController, UISearchBarDelegate {
convenience init(delegate: PlacesAutocompleteViewControllerDelegate) {
let tableViewController = PlacesAutocompleteContainer(
delegate: delegate
)
self.init(searchResultsController: tableViewController)
self.searchResultsUpdater = tableViewController
self.hidesNavigationBarDuringPresentation = false
self.definesPresentationContext = true
self.searchBar.placeholder = searchBarPlaceholder
}
}
private extension ShowAddressViewController {
@objc func streetAddressTextFieldEditingDidBegin() {
present(placesSearchController, animated: true, completion: nil)
}
}
我希望让文本字段飞到导航栏,而不是从顶部下拉搜索。我所追求的是与 iOS 11 文件应用程序相同的效果:
它在屏幕中间有一个文本字段,当您点击它时,它会动画到导航栏。但就我而言,文本字段在屏幕中的位置较低,最初并不是导航栏的一部分。
- 在 XIB 中创建一个 UIView。将其命名为 searchView。
在同一 xib 中的 UIView 上方添加 UIButton 并将其命名为 btnSearch。像下面一样

在 ViewDidLoad 中设置搜索控制器如下:
func setupSearchController() {
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
definesPresentationContext = true
self.searchController.dimsBackgroundDuringPresentation = false
self.searchController.searchBar.sizeToFit()
searchView.addSubview(self.searchController.searchBar)
self.searchController.searchBar.isHidden = true
self.searchController.searchBar.tintColor = UIColor.white
self.searchController.searchBar.showsCancelButton = false
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).tintColor = session?.makeColor(fromHexString: TYPE_COLOR, alpha: 1.0)
self.searchController.searchBar.isTranslucent = false
self.searchController.searchBar.barTintColor = UIColor(red: 239.0 / 255.0, green: 135.0 / 255.0, blue: 1.0 / 255.0, alpha: 1.0)
}
此方法将在搜索视图中以编程方式设置搜索控制器。
现在您只需以编程方式显示 searchcontroller。在步骤 2 中添加按钮的单击方法。在方法名称下方调用此方法名称 showsearchAnimation:
func ShowSeachAnimation() {
searchFrame = searchView.frame (add one global variable "searchFrame" in controller which saves searchView.frame so it will be used when cancel button clicked on search)
self.btnSearch.isHidden = true
var yAxis : CGFloat = 20
if #available(iOS 11 , *) {
yAxis = 8
} else {
yAxis = 20
}
searchView.translatesAutoresizingMaskIntoConstraints = true
searchView.frame = CGRect(x: 0, y: yAxis, width: view.frame.size.width, height: view.frame.size.height - yAxis)
self.searchController.searchBar.isHidden = false
self.searchController.searchBar.becomeFirstResponder()
self.searchController.searchBar.showsCancelButton = true
self.searchBar(self.searchController.searchBar, textDidChange: "")
searchView.layoutIfNeeded()
}
为了隐藏搜索栏,在搜索控制器委托中添加名为"searchbarcancelbuttonclicked"的搜索隐藏方法:
func searchViewHideAnimation() {
self.removeNavigationBar()
self.navigationController?.isNavigationBarHidden = false
self.searchController.searchBar.text = " "
self.searchController.searchBar.isHidden = true
UIView.animate(withDuration: 0.3, animations: {() -> Void in
self.searchView.frame = self.searchFrame!
self.btnSearch.isHidden = false
}, completion: {(_ finished: Bool) -> Void in
self.searchView.layoutIfNeeded()
})
}
UISearchController
UISearchController 是一个很难自定义的组件。根据我的经验,我可以说,最好按原样使用它,不要进行任何激烈或重要的定制。否则,自定义可能会导致代码混乱、全局状态变量、UIView 层次结构的运行时技巧等。
如果仍然需要特定行为,最好从头开始实现搜索控制器或使用第三方控制器。
默认实现
看起来 UISearchController 旨在与 table header 视图中安装的 UITableView 和 UISearchBar 结合使用。甚至苹果官方代码示例和文档也提供了这样的用法示例(参见UISearchController)。尝试在其他地方安装 UISearchBar 通常会导致许多丑陋的副作用,包括搜索栏框架、位置、抽动动画、方向变化等。
从 iOS 11 开始,UINavigationItem 得到 searchController 属性。它允许将搜索控制器集成到您的导航界面中,因此搜索看起来就像 iOS 文件或 iOS AppStore 应用程序。 UISearchController 的行为仍然与另一个组件耦合,但我相信它比一直与 UITableView 耦合更好。
可能的解决方案
在我看来,您的问题有多种可能的解决方案。我将按照需要实现的工作量增加的顺序提供它们:
- 如果您仍想使用 UISearchController,请考虑按原样使用它,不要进行重大自定义。 Apple 提供示例代码,演示如何使用 UISearchController(参见 Table Search with UISearchController)。
- 有几个第三方库可能更灵活,具有许多附加功能。例如:YNSearch, PYSearch。请看一下。
- 随着 UISearchController 的呈现,您可以尝试将 alpha 从 1 更改为 0 来向上移动 UITextField。这会让人感觉 UITextField 正在平滑地转换为 UISearchBar。 Chris Slowik 提供的文章中描述了这种方法(请参阅对原始 post 的评论)。我唯一要改进的是使用过渡协调器的动画(参见示例 here),它将使同步时间的动画更流畅。最终实施也将更加清晰。
- 作为一种选择,您可以尝试仅使用 UISearchBar 或什至普通的 UITextField 来设计您自己的搜索控制器。
- 您可以子类化 UISearchController 并添加 UITextField object。 UISearchController 符合 UIViewControllerAnimatedTransitioning 和 UIViewControllerTransitioningDelegate 协议,其中 UITextFiled 可以与过渡动画一起删除或添加。
希望对您有所帮助。
更新:
我已经实施了我在第 3 点中描述的方法。这是它的工作原理:
您可以找到代码片段 here。请注意,这只是代码示例,可能存在未处理的情况。然后检查两次。
您可以尝试使用我的 https://github.com/Blackjacx/SHSearchBar,它基本上就是您的文本字段。您可以在文本字段编辑开始时向左侧、右侧和顶部添加约束并调整顶部约束。同时,您可以使用覆盖视图隐藏动画导航栏并使背景变灰。这样你就可以最大限度地控制你的动画,而且这个方法并不像听起来那么困难。
我正在展示 UISearchController
来自嵌入导航控制器的控制器。出现默认动画,其中搜索框从导航栏顶部下拉。
这对我来说不是一个好的用户体验,因为我会在用户点击屏幕中间的 UITextField
时显示搜索。我想做的是让 UITextField
浮动到顶部并变成搜索框,但我不知道该怎么做。
这是我的:
class PlacesSearchController: UISearchController, UISearchBarDelegate {
convenience init(delegate: PlacesAutocompleteViewControllerDelegate) {
let tableViewController = PlacesAutocompleteContainer(
delegate: delegate
)
self.init(searchResultsController: tableViewController)
self.searchResultsUpdater = tableViewController
self.hidesNavigationBarDuringPresentation = false
self.definesPresentationContext = true
self.searchBar.placeholder = searchBarPlaceholder
}
}
private extension ShowAddressViewController {
@objc func streetAddressTextFieldEditingDidBegin() {
present(placesSearchController, animated: true, completion: nil)
}
}
我希望让文本字段飞到导航栏,而不是从顶部下拉搜索。我所追求的是与 iOS 11 文件应用程序相同的效果:
它在屏幕中间有一个文本字段,当您点击它时,它会动画到导航栏。但就我而言,文本字段在屏幕中的位置较低,最初并不是导航栏的一部分。
- 在 XIB 中创建一个 UIView。将其命名为 searchView。
在同一 xib 中的 UIView 上方添加 UIButton 并将其命名为 btnSearch。像下面一样 
在 ViewDidLoad 中设置搜索控制器如下:
func setupSearchController() { self.searchController = UISearchController(searchResultsController: nil) self.searchController.delegate = self self.searchController.searchBar.delegate = self definesPresentationContext = true self.searchController.dimsBackgroundDuringPresentation = false self.searchController.searchBar.sizeToFit() searchView.addSubview(self.searchController.searchBar) self.searchController.searchBar.isHidden = true self.searchController.searchBar.tintColor = UIColor.white self.searchController.searchBar.showsCancelButton = false UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).tintColor = session?.makeColor(fromHexString: TYPE_COLOR, alpha: 1.0) self.searchController.searchBar.isTranslucent = false self.searchController.searchBar.barTintColor = UIColor(red: 239.0 / 255.0, green: 135.0 / 255.0, blue: 1.0 / 255.0, alpha: 1.0) }
此方法将在搜索视图中以编程方式设置搜索控制器。
现在您只需以编程方式显示 searchcontroller。在步骤 2 中添加按钮的单击方法。在方法名称下方调用此方法名称 showsearchAnimation:
func ShowSeachAnimation() { searchFrame = searchView.frame (add one global variable "searchFrame" in controller which saves searchView.frame so it will be used when cancel button clicked on search) self.btnSearch.isHidden = true var yAxis : CGFloat = 20 if #available(iOS 11 , *) { yAxis = 8 } else { yAxis = 20 } searchView.translatesAutoresizingMaskIntoConstraints = true searchView.frame = CGRect(x: 0, y: yAxis, width: view.frame.size.width, height: view.frame.size.height - yAxis) self.searchController.searchBar.isHidden = false self.searchController.searchBar.becomeFirstResponder() self.searchController.searchBar.showsCancelButton = true self.searchBar(self.searchController.searchBar, textDidChange: "") searchView.layoutIfNeeded() }
为了隐藏搜索栏,在搜索控制器委托中添加名为"searchbarcancelbuttonclicked"的搜索隐藏方法:
func searchViewHideAnimation() { self.removeNavigationBar() self.navigationController?.isNavigationBarHidden = false self.searchController.searchBar.text = " " self.searchController.searchBar.isHidden = true UIView.animate(withDuration: 0.3, animations: {() -> Void in self.searchView.frame = self.searchFrame! self.btnSearch.isHidden = false }, completion: {(_ finished: Bool) -> Void in self.searchView.layoutIfNeeded() }) }
UISearchController
UISearchController 是一个很难自定义的组件。根据我的经验,我可以说,最好按原样使用它,不要进行任何激烈或重要的定制。否则,自定义可能会导致代码混乱、全局状态变量、UIView 层次结构的运行时技巧等。 如果仍然需要特定行为,最好从头开始实现搜索控制器或使用第三方控制器。
默认实现
看起来 UISearchController 旨在与 table header 视图中安装的 UITableView 和 UISearchBar 结合使用。甚至苹果官方代码示例和文档也提供了这样的用法示例(参见UISearchController)。尝试在其他地方安装 UISearchBar 通常会导致许多丑陋的副作用,包括搜索栏框架、位置、抽动动画、方向变化等。
从 iOS 11 开始,UINavigationItem 得到 searchController 属性。它允许将搜索控制器集成到您的导航界面中,因此搜索看起来就像 iOS 文件或 iOS AppStore 应用程序。 UISearchController 的行为仍然与另一个组件耦合,但我相信它比一直与 UITableView 耦合更好。
可能的解决方案
在我看来,您的问题有多种可能的解决方案。我将按照需要实现的工作量增加的顺序提供它们:
- 如果您仍想使用 UISearchController,请考虑按原样使用它,不要进行重大自定义。 Apple 提供示例代码,演示如何使用 UISearchController(参见 Table Search with UISearchController)。
- 有几个第三方库可能更灵活,具有许多附加功能。例如:YNSearch, PYSearch。请看一下。
- 随着 UISearchController 的呈现,您可以尝试将 alpha 从 1 更改为 0 来向上移动 UITextField。这会让人感觉 UITextField 正在平滑地转换为 UISearchBar。 Chris Slowik 提供的文章中描述了这种方法(请参阅对原始 post 的评论)。我唯一要改进的是使用过渡协调器的动画(参见示例 here),它将使同步时间的动画更流畅。最终实施也将更加清晰。
- 作为一种选择,您可以尝试仅使用 UISearchBar 或什至普通的 UITextField 来设计您自己的搜索控制器。
- 您可以子类化 UISearchController 并添加 UITextField object。 UISearchController 符合 UIViewControllerAnimatedTransitioning 和 UIViewControllerTransitioningDelegate 协议,其中 UITextFiled 可以与过渡动画一起删除或添加。
希望对您有所帮助。
更新:
我已经实施了我在第 3 点中描述的方法。这是它的工作原理:
您可以找到代码片段 here。请注意,这只是代码示例,可能存在未处理的情况。然后检查两次。
您可以尝试使用我的 https://github.com/Blackjacx/SHSearchBar,它基本上就是您的文本字段。您可以在文本字段编辑开始时向左侧、右侧和顶部添加约束并调整顶部约束。同时,您可以使用覆盖视图隐藏动画导航栏并使背景变灰。这样你就可以最大限度地控制你的动画,而且这个方法并不像听起来那么困难。