forked from zelo72/mastodon-ios
feat: make search bar works on iPad
This commit is contained in:
parent
1da803fb97
commit
9d66119f9e
|
@ -69,8 +69,8 @@
|
|||
"repositoryURL": "https://github.com/MainasuK/FPSIndicator.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "b2a002d689c400485f2ba41f9e71e15f7b99764a",
|
||||
"version": "1.0.1"
|
||||
"revision": "e4a5067ccd5293b024c767f09e51056afd4a4796",
|
||||
"version": "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -11,6 +11,12 @@ import GameplayKit
|
|||
import MastodonSDK
|
||||
import UIKit
|
||||
|
||||
final class HeightFixedSearchBar: UISearchBar {
|
||||
override var intrinsicContentSize: CGSize {
|
||||
return CGSize(width: CGFloat.greatestFiniteMagnitude, height: 44)
|
||||
}
|
||||
}
|
||||
|
||||
final class SearchViewController: UIViewController, NeedsDependency {
|
||||
|
||||
let logger = Logger(subsystem: "Search", category: "UI")
|
||||
|
@ -41,6 +47,11 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
|||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
private(set) lazy var viewModel = SearchViewModel(context: context)
|
||||
|
||||
// use AutoLayout could set search bar margin automatically to
|
||||
// layout alongside with split mode button (on iPad)
|
||||
let titleViewContainer = UIView()
|
||||
let searchBar = HeightFixedSearchBar()
|
||||
|
||||
// recommend
|
||||
let scrollView: UIScrollView = {
|
||||
|
@ -112,6 +123,10 @@ extension SearchViewController {
|
|||
super.viewDidAppear(animated)
|
||||
|
||||
viewModel.viewDidAppeared.send()
|
||||
|
||||
// note:
|
||||
// need set alpha because (maybe) SDK forget set alpha back
|
||||
titleViewContainer.alpha = 1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,10 +136,17 @@ extension SearchViewController {
|
|||
}
|
||||
|
||||
private func setupSearchBar() {
|
||||
let searchBar = UISearchBar()
|
||||
searchBar.placeholder = L10n.Scene.Search.SearchBar.placeholder
|
||||
searchBar.delegate = self
|
||||
navigationItem.titleView = searchBar
|
||||
searchBar.translatesAutoresizingMaskIntoConstraints = false
|
||||
titleViewContainer.addSubview(searchBar)
|
||||
NSLayoutConstraint.activate([
|
||||
searchBar.topAnchor.constraint(equalTo: titleViewContainer.topAnchor),
|
||||
searchBar.leadingAnchor.constraint(equalTo: titleViewContainer.leadingAnchor),
|
||||
searchBar.trailingAnchor.constraint(equalTo: titleViewContainer.trailingAnchor),
|
||||
searchBar.bottomAnchor.constraint(equalTo: titleViewContainer.bottomAnchor),
|
||||
])
|
||||
navigationItem.titleView = titleViewContainer
|
||||
|
||||
searchBarTapPublisher
|
||||
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: false)
|
||||
|
|
|
@ -10,6 +10,8 @@ import UIKit
|
|||
import Combine
|
||||
import Pageboy
|
||||
|
||||
// Fake search bar not works on iPad with UISplitViewController
|
||||
// check device and fallback to standard UISearchController
|
||||
final class SearchDetailViewController: PageboyViewController, NeedsDependency {
|
||||
|
||||
let logger = Logger(subsystem: "SearchDetail", category: "UI")
|
||||
|
@ -19,6 +21,10 @@ final class SearchDetailViewController: PageboyViewController, NeedsDependency {
|
|||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
let isPhoneDevice: Bool = {
|
||||
return UIDevice.current.userInterfaceIdiom == .phone
|
||||
}()
|
||||
|
||||
var viewModel: SearchDetailViewModel!
|
||||
var viewControllers: [SearchResultViewController]!
|
||||
|
@ -39,8 +45,22 @@ final class SearchDetailViewController: PageboyViewController, NeedsDependency {
|
|||
navigationBar.setItems([navigationItem], animated: false)
|
||||
return navigationBar
|
||||
}()
|
||||
let searchBar: UISearchBar = {
|
||||
let searchBar = UISearchBar()
|
||||
|
||||
let searchController: UISearchController = {
|
||||
let searchController = UISearchController()
|
||||
searchController.automaticallyShowsScopeBar = false
|
||||
searchController.dimsBackgroundDuringPresentation = false
|
||||
return searchController
|
||||
}()
|
||||
private(set) lazy var searchBar: UISearchBar = {
|
||||
let searchBar: UISearchBar
|
||||
if isPhoneDevice {
|
||||
searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
|
||||
} else {
|
||||
searchBar = searchController.searchBar
|
||||
searchController.automaticallyShowsScopeBar = false
|
||||
searchController.searchBar.setShowsScope(true, animated: false)
|
||||
}
|
||||
searchBar.placeholder = L10n.Scene.Search.SearchBar.placeholder
|
||||
searchBar.scopeButtonTitles = SearchDetailViewModel.SearchScope.allCases.map { $0.segmentedControlTitle }
|
||||
searchBar.sizeToFit()
|
||||
|
@ -71,48 +91,27 @@ extension SearchDetailViewController {
|
|||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
navigationBar.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(navigationBar)
|
||||
NSLayoutConstraint.activate([
|
||||
navigationBar.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
|
||||
navigationBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
navigationBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
])
|
||||
setupSearchBar()
|
||||
navigationBar.layer.observe(\.bounds, options: [.new]) { [weak self] navigationBar, _ in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.navigationBarFrame.value = navigationBar.frame
|
||||
}
|
||||
.store(in: &observations)
|
||||
|
||||
navigationBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.insertSubview(navigationBarBackgroundView, belowSubview: navigationBar)
|
||||
NSLayoutConstraint.activate([
|
||||
navigationBarBackgroundView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
navigationBarBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
navigationBarBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
navigationBarBackgroundView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor),
|
||||
])
|
||||
|
||||
navigationBarVisualEffectBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.insertSubview(navigationBarVisualEffectBackgroundView, belowSubview: navigationBarBackgroundView)
|
||||
NSLayoutConstraint.activate([
|
||||
navigationBarVisualEffectBackgroundView.topAnchor.constraint(equalTo: navigationBarBackgroundView.topAnchor),
|
||||
navigationBarVisualEffectBackgroundView.leadingAnchor.constraint(equalTo: navigationBarBackgroundView.leadingAnchor),
|
||||
navigationBarVisualEffectBackgroundView.trailingAnchor.constraint(equalTo: navigationBarBackgroundView.trailingAnchor),
|
||||
navigationBarVisualEffectBackgroundView.bottomAnchor.constraint(equalTo: navigationBarBackgroundView.bottomAnchor),
|
||||
])
|
||||
|
||||
|
||||
addChild(searchHistoryViewController)
|
||||
searchHistoryViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(searchHistoryViewController.view)
|
||||
searchHistoryViewController.didMove(toParent: self)
|
||||
NSLayoutConstraint.activate([
|
||||
searchHistoryViewController.view.topAnchor.constraint(equalTo: navigationBarBackgroundView.bottomAnchor),
|
||||
searchHistoryViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
searchHistoryViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
searchHistoryViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
if isPhoneDevice {
|
||||
NSLayoutConstraint.activate([
|
||||
searchHistoryViewController.view.topAnchor.constraint(equalTo: navigationBarBackgroundView.bottomAnchor),
|
||||
searchHistoryViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
searchHistoryViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
searchHistoryViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
} else {
|
||||
NSLayoutConstraint.activate([
|
||||
searchHistoryViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
searchHistoryViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
searchHistoryViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
searchHistoryViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
transition = Transition(style: .fade, duration: 0.1)
|
||||
isScrollEnabled = false
|
||||
|
@ -215,33 +214,83 @@ extension SearchDetailViewController {
|
|||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
navigationController?.setNavigationBarHidden(true, animated: animated)
|
||||
searchBar.setShowsScope(true, animated: false)
|
||||
searchBar.setNeedsLayout()
|
||||
searchBar.layoutIfNeeded()
|
||||
if isPhoneDevice {
|
||||
navigationController?.setNavigationBarHidden(true, animated: animated)
|
||||
searchBar.setShowsScope(true, animated: false)
|
||||
searchBar.setNeedsLayout()
|
||||
searchBar.layoutIfNeeded()
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if !isModal {
|
||||
// prevent bar restore conflict with modal style issue
|
||||
navigationController?.setNavigationBarHidden(false, animated: animated)
|
||||
if isPhoneDevice {
|
||||
if !isModal {
|
||||
// prevent bar restore conflict with modal style issue
|
||||
navigationController?.setNavigationBarHidden(false, animated: animated)
|
||||
}
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
searchBar.setShowsCancelButton(true, animated: animated)
|
||||
searchBar.becomeFirstResponder()
|
||||
if isPhoneDevice {
|
||||
searchBar.setShowsCancelButton(true, animated: animated)
|
||||
searchBar.becomeFirstResponder()
|
||||
} else {
|
||||
searchController.isActive = true
|
||||
searchController.searchBar.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SearchDetailViewController {
|
||||
private func setupSearchBar() {
|
||||
navigationBar.topItem?.titleView = searchBar
|
||||
if isPhoneDevice {
|
||||
navigationBar.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(navigationBar)
|
||||
NSLayoutConstraint.activate([
|
||||
navigationBar.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
|
||||
navigationBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
navigationBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
])
|
||||
navigationBar.topItem?.titleView = searchBar
|
||||
navigationBar.layer.observe(\.bounds, options: [.new]) { [weak self] navigationBar, _ in
|
||||
guard let self = self else { return }
|
||||
self.viewModel.navigationBarFrame.value = navigationBar.frame
|
||||
}
|
||||
.store(in: &observations)
|
||||
|
||||
navigationBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.insertSubview(navigationBarBackgroundView, belowSubview: navigationBar)
|
||||
NSLayoutConstraint.activate([
|
||||
navigationBarBackgroundView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
navigationBarBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
navigationBarBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
navigationBarBackgroundView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor),
|
||||
])
|
||||
|
||||
navigationBarVisualEffectBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.insertSubview(navigationBarVisualEffectBackgroundView, belowSubview: navigationBarBackgroundView)
|
||||
NSLayoutConstraint.activate([
|
||||
navigationBarVisualEffectBackgroundView.topAnchor.constraint(equalTo: navigationBarBackgroundView.topAnchor),
|
||||
navigationBarVisualEffectBackgroundView.leadingAnchor.constraint(equalTo: navigationBarBackgroundView.leadingAnchor),
|
||||
navigationBarVisualEffectBackgroundView.trailingAnchor.constraint(equalTo: navigationBarBackgroundView.trailingAnchor),
|
||||
navigationBarVisualEffectBackgroundView.bottomAnchor.constraint(equalTo: navigationBarBackgroundView.bottomAnchor),
|
||||
])
|
||||
} else {
|
||||
navigationItem.setHidesBackButton(true, animated: false)
|
||||
navigationItem.titleView = nil
|
||||
navigationItem.searchController = searchController
|
||||
searchController.searchBar.sizeToFit()
|
||||
}
|
||||
|
||||
searchBar.delegate = self
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue