mastodon-ios/Mastodon/Scene/Search/Search/SearchViewController.swift

155 lines
5.5 KiB
Swift

//
// SearchViewController.swift
// Mastodon
//
// Created by sxiaojian on 2021/3/31.
//
import Combine
import GameplayKit
import MastodonSDK
import UIKit
import MastodonAsset
import MastodonCore
import MastodonLocalization
final class HeightFixedSearchBar: UISearchBar {
override var intrinsicContentSize: CGSize {
return CGSize(width: CGFloat.greatestFiniteMagnitude, height: 36)
}
}
final class SearchViewController: UIViewController, NeedsDependency {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var searchTransitionController = SearchTransitionController()
var disposeBag = Set<AnyCancellable>()
var viewModel: SearchViewModel?
// use AutoLayout could set search bar margin automatically to
// layout alongside with split mode button (on iPad)
let titleViewContainer = UIView()
let searchBar = HeightFixedSearchBar()
// value is the initial search text to set
let searchBarTapPublisher = PassthroughSubject<String, Never>()
private(set) lazy var discoveryViewController: DiscoveryViewController? = {
guard let authContext = viewModel?.authContext else { return nil }
let viewController = DiscoveryViewController()
viewController.context = context
viewController.coordinator = coordinator
viewController.viewModel = .init(
context: context,
coordinator: coordinator,
authContext: authContext
)
return viewController
}()
}
extension SearchViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupAppearance()
title = L10n.Scene.Search.title
setupSearchBar()
guard let discoveryViewController = self.discoveryViewController else { return }
addChild(discoveryViewController)
discoveryViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(discoveryViewController.view)
discoveryViewController.view.pinToParent()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewModel?.viewDidAppeared.send()
// note:
// need set alpha because (maybe) SDK forget set alpha back
titleViewContainer.alpha = 1
}
}
extension SearchViewController {
private func setupAppearance() {
view.backgroundColor = .systemGroupedBackground
// Match the DiscoveryViewController tab color and remove the double separator.
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithOpaqueBackground()
navigationBarAppearance.backgroundColor = .systemBackground
navigationBarAppearance.shadowColor = nil
navigationItem.standardAppearance = navigationBarAppearance
navigationItem.scrollEdgeAppearance = navigationBarAppearance
navigationItem.compactAppearance = navigationBarAppearance
navigationItem.compactScrollEdgeAppearance = navigationBarAppearance
}
private func setupSearchBar() {
searchBar.placeholder = L10n.Scene.Search.SearchBar.placeholder
searchBar.delegate = self
searchBar.translatesAutoresizingMaskIntoConstraints = false
titleViewContainer.addSubview(searchBar)
searchBar.pinToParent()
searchBar.setContentHuggingPriority(.required, for: .horizontal)
searchBar.setContentHuggingPriority(.required, for: .vertical)
navigationItem.titleView = titleViewContainer
// navigationItem.titleView = searchBar
searchBarTapPublisher
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: false)
.sink { [weak self] initialText in
guard let self = self else { return }
// push to search detail
guard let authContext = self.viewModel?.authContext else { return }
let searchDetailViewModel = SearchDetailViewModel(authContext: authContext, initialSearchText: initialText)
searchDetailViewModel.needsBecomeFirstResponder = true
self.navigationController?.delegate = self.searchTransitionController
// FIXME:
// use `.customPush(animated: false)` false to disable navigation bar animation for searchBar layout
// but that should be a fade transition whe fixed size searchBar
_ = self.coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .customPush(animated: false))
}
.store(in: &disposeBag)
}
}
// MARK: - UISearchBarDelegate
extension SearchViewController: UISearchBarDelegate {
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
searchBarTapPublisher.send("")
return false
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchBar.text = ""
searchBarTapPublisher.send(searchText)
}
}
// MARK: - UISearchControllerDelegate
extension SearchViewController: UISearchControllerDelegate {
func willDismissSearchController(_ searchController: UISearchController) {
searchController.isActive = true
}
}
// MARK: - ScrollViewContainer
extension SearchViewController: ScrollViewContainer {
var scrollView: UIScrollView {
discoveryViewController?.scrollView ?? UIScrollView()
}
func scrollToTop(animated: Bool) {
discoveryViewController?.scrollToTop(animated: animated)
}
}