交互式下拉搜索栏
我正在尝试使用 UIViewPropertyAnimator 实现交互式下拉搜索栏。但我是 iOS 开发新手。所以我犯了很多错误。请查看此代码,给我任何解决方案。
import UIKit
class ViewController: UIViewController {
private let searchController = UISearchController(searchResultsController: nil)
let scrollView = UIScrollView()
var transitionInProgress: Bool = false
var searchAnimator: UIViewPropertyAnimator?
var isSearchBarShowing: Bool = false
var cachedSearchBarFrame: CGRect = .init()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Home"
cachedSearchBarFrame = searchController.searchBar.frame
self.navigationController?.navigationBar.backgroundColor = .white
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.delegate = self
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search"
searchController.delegate = self
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(searchButtonTapped))
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
navigationItem.titleView = searchController.searchBar
navigationItem.titleView?.alpha = 0
searchAnimator = UIViewPropertyAnimator(duration: 2, curve: .easeIn) {
self.searchController.searchBar.frame = self.cachedSearchBarFrame
self.navigationItem.titleView?.alpha = 1
}
searchAnimator?.addCompletion { position in
if position == .end {
self.isSearchBarShowing = true
} else if position == .start {
self.searchAnimator?.isReversed = false
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = .init(width: self.view.frame.width, height: 2000)
}
@objc func searchButtonTapped() {
if isSearchBarShowing {
hideSearchBar()
} else {
showSearchBar()
}
}
func showSearchBar() {
isSearchBarShowing = true
searchController.searchBar.frame = CGRect(x: 0, y: -self.cachedSearchBarFrame.height, width: self.cachedSearchBarFrame.width, height: self.cachedSearchBarFrame.height)
self.navigationItem.titleView?.alpha = 0
// Apply animation
UIView.animate(withDuration: 1) {
self.searchController.searchBar.frame = self.cachedSearchBarFrame
self.navigationItem.titleView?.alpha = 1
}
}
func hideSearchBar() {
isSearchBarShowing = false
UIView.animate(withDuration: 1) {
self.searchController.searchBar.frame = CGRect(x: 0, y: -self.cachedSearchBarFrame.height, width: self.cachedSearchBarFrame.width, height: self.cachedSearchBarFrame.height)
self.navigationItem.titleView?.alpha = 0
}
}
}
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
// Implement your search logic here
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pullThreshold: CGFloat = -100
if scrollView.contentOffset.y + scrollView.contentInset.top < pullThreshold {
if !transitionInProgress && scrollView.isDragging && scrollView.isTracking {
let fractionComplete = -(scrollView.contentOffset.y + 200) / 100
searchAnimator?.fractionComplete = min(max(fractionComplete, 0), 1)
print(min(max(fractionComplete, 0), 1))
searchAnimator?.startAnimation()
}
}
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let fractionComplete = -(scrollView.contentOffset.y + 200) / 100
if fractionComplete < 1 && fractionComplete > 0 {
searchAnimator?.isReversed = true
searchAnimator?.startAnimation()
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
searchAnimator?.stopAnimation(true)
if !isSearchBarShowing {
searchController.searchBar.frame = CGRect(x: 0, y: -self.cachedSearchBarFrame.height, width: self.cachedSearchBarFrame.width, height: self.cachedSearchBarFrame.height)
self.navigationItem.titleView?.alpha = 0
searchAnimator?.fractionComplete = 0
}
}
}
extension ViewController: UISearchControllerDelegate {
}
在这段代码中,我尝试实现一个下拉搜索栏,但它在第一次下拉时效果很好,但不适用于下一次后续拉动。请修复此代码中的错误。
UISearchController
在控制器中呈现时具有默认动画。因此,你不必计算这些帧、alpha 等。而且它还在内部处理 showingState
。我更喜欢这种方法:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pullThreshold: CGFloat = -100
if scrollView.contentOffset.y + scrollView.contentInset.top < pullThreshold {
//Keep your threshold as you wish
showSearchBar()
}
}
func showSearchBar() {
guard self.presentedViewController !== searchController else { return }
present(searchController, animated: true) //<- here
}
输出: