co-maps/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapHeaderView.swift
2025-11-22 13:58:55 +01:00

151 lines
5.4 KiB
Swift

protocol SearchOnMapHeaderViewDelegate: UISearchBarDelegate {
func cancelButtonDidTap()
func grabberDidTap()
}
final class SearchOnMapHeaderView: UIView {
weak var delegate: SearchOnMapHeaderViewDelegate? {
didSet {
searchBar.delegate = delegate
}
}
private enum Constants {
static let searchBarHeight: CGFloat = 36
static let searchBarInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 10, bottom: 10, right: 0)
static let grabberHeight: CGFloat = 5
static let grabberWidth: CGFloat = 36
static let grabberTopMargin: CGFloat = 5
static let cancelButtonInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 16)
}
private let grabberView = UIView()
private let grabberTapHandlerView = UIView()
private let searchBar = UISearchBar()
private let cancelButton = UIButton()
private let cancelContainer = UIView()
private var separator: UIView?
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
layoutView()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
setStyle(.background)
setupGrabberView()
setupGrabberTapHandlerView()
setupSearchBar()
setupCancelButton()
}
private func setupGrabberView() {
grabberView.setStyle(.grabber)
iPadSpecific { [weak self] in
self?.grabberView.isHidden = true
}
}
private func setupGrabberTapHandlerView() {
grabberTapHandlerView.backgroundColor = .clear
iPhoneSpecific {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(grabberDidTap))
grabberTapHandlerView.addGestureRecognizer(tapGesture)
}
}
private func setupSearchBar() {
searchBar.setStyle(.defaultSearchBar)
searchBar.placeholder = L("search")
searchBar.showsCancelButton = false
searchBar.searchTextField.clearButtonMode = .always
searchBar.returnKeyType = .search
searchBar.searchTextField.enablesReturnKeyAutomatically = true
searchBar.backgroundImage = UIImage()
}
private func setupCancelButton() {
cancelContainer.setStyle(.background)
cancelButton.setStyle(.searchCancelButton)
cancelButton.setTitle(L("cancel"), for: .normal)
cancelButton.addTarget(self, action: #selector(cancelButtonDidTap), for: .touchUpInside)
}
private func layoutView() {
addSubview(grabberView)
addSubview(grabberTapHandlerView)
addSubview(searchBar)
addSubview(cancelContainer)
cancelContainer.addSubview(cancelButton)
separator = addSeparator(.bottom)
grabberView.translatesAutoresizingMaskIntoConstraints = false
grabberTapHandlerView.translatesAutoresizingMaskIntoConstraints = false
grabberTapHandlerView.setContentHuggingPriority(.defaultLow, for: .vertical)
searchBar.translatesAutoresizingMaskIntoConstraints = false
cancelContainer.translatesAutoresizingMaskIntoConstraints = false
cancelButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
grabberView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.grabberTopMargin),
grabberView.centerXAnchor.constraint(equalTo: centerXAnchor),
grabberView.widthAnchor.constraint(equalToConstant: Constants.grabberWidth),
grabberView.heightAnchor.constraint(equalToConstant: Constants.grabberHeight),
grabberTapHandlerView.topAnchor.constraint(equalTo: grabberView.bottomAnchor),
grabberTapHandlerView.leadingAnchor.constraint(equalTo: leadingAnchor),
grabberTapHandlerView.trailingAnchor.constraint(equalTo: trailingAnchor),
grabberTapHandlerView.bottomAnchor.constraint(equalTo: searchBar.topAnchor),
searchBar.topAnchor.constraint(equalTo: grabberView.bottomAnchor, constant: Constants.searchBarInsets.top),
searchBar.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.searchBarInsets.left),
searchBar.trailingAnchor.constraint(equalTo: cancelContainer.leadingAnchor),
searchBar.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.searchBarInsets.bottom),
searchBar.heightAnchor.constraint(equalToConstant: Constants.searchBarHeight),
cancelContainer.trailingAnchor.constraint(equalTo: trailingAnchor),
cancelContainer.topAnchor.constraint(equalTo: searchBar.topAnchor),
cancelContainer.bottomAnchor.constraint(equalTo: searchBar.bottomAnchor),
cancelButton.topAnchor.constraint(equalTo: cancelContainer.topAnchor),
cancelButton.leadingAnchor.constraint(equalTo: cancelContainer.leadingAnchor, constant: Constants.cancelButtonInsets.left),
cancelButton.trailingAnchor.constraint(equalTo: cancelContainer.trailingAnchor, constant: -Constants.cancelButtonInsets.right),
cancelButton.bottomAnchor.constraint(equalTo: cancelContainer.bottomAnchor),
])
}
@objc private func grabberDidTap() {
delegate?.grabberDidTap()
}
@objc private func cancelButtonDidTap() {
delegate?.cancelButtonDidTap()
}
func setSearchText(_ text: String) {
searchBar.text = text
}
func setIsSearching(_ isSearching: Bool) {
if isSearching {
searchBar.becomeFirstResponder()
} else if searchBar.isFirstResponder {
searchBar.resignFirstResponder()
}
}
var searchQuery: SearchQuery {
SearchQuery(searchBar.text ?? "", locale: searchBar.textInputMode?.primaryLanguage, source: .typedText)
}
func setSeparatorHidden(_ hidden: Bool) {
separator?.isHidden = hidden
}
}