DiscoveryViewController.swift 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. //
  2. // DiscoveryViewController.swift
  3. // Mastodon
  4. //
  5. // Created by MainasuK on 2022-4-12.
  6. //
  7. import os.log
  8. import UIKit
  9. import Combine
  10. import Tabman
  11. import Pageboy
  12. import MastodonAsset
  13. import MastodonCore
  14. import MastodonUI
  15. public class DiscoveryViewController: TabmanViewController, NeedsDependency {
  16. public static let containerViewMarginForRegularHorizontalSizeClass: CGFloat = 64
  17. public static let containerViewMarginForCompactHorizontalSizeClass: CGFloat = 16
  18. var disposeBag = Set<AnyCancellable>()
  19. let logger = Logger(subsystem: "DiscoveryViewController", category: "ViewController")
  20. weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
  21. weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
  22. var viewModel: DiscoveryViewModel!
  23. private(set) lazy var buttonBar: TMBar.ButtonBar = {
  24. let buttonBar = TMBar.ButtonBar()
  25. buttonBar.backgroundView.style = .custom(view: buttonBarBackgroundView)
  26. buttonBar.layout.interButtonSpacing = 0
  27. buttonBar.layout.contentInset = .zero
  28. buttonBar.indicator.backgroundColor = Asset.Colors.Label.primary.color
  29. buttonBar.indicator.weight = .custom(value: 2)
  30. return buttonBar
  31. }()
  32. let buttonBarBackgroundView: UIView = {
  33. let view = UIView()
  34. let barBottomLine = UIView.separatorLine
  35. barBottomLine.backgroundColor = Asset.Colors.Label.secondary.color.withAlphaComponent(0.5)
  36. barBottomLine.translatesAutoresizingMaskIntoConstraints = false
  37. view.addSubview(barBottomLine)
  38. NSLayoutConstraint.activate([
  39. barBottomLine.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  40. barBottomLine.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  41. barBottomLine.bottomAnchor.constraint(equalTo: view.bottomAnchor),
  42. barBottomLine.heightAnchor.constraint(equalToConstant: 2).priority(.required - 1),
  43. ])
  44. return view
  45. }()
  46. func customizeButtonBarAppearance() {
  47. // The implmention use CATextlayer. Adapt for Dark Mode without dynamic colors
  48. // Needs trigger update when `userInterfaceStyle` chagnes
  49. let userInterfaceStyle = traitCollection.userInterfaceStyle
  50. buttonBar.buttons.customize { button in
  51. switch userInterfaceStyle {
  52. case .dark:
  53. // Asset.Colors.Label.primary.color
  54. button.selectedTintColor = UIColor(red: 238.0/255.0, green: 238.0/255.0, blue: 238.0/255.0, alpha: 1.0)
  55. // Asset.Colors.Label.secondary.color
  56. button.tintColor = UIColor(red: 151.0/255.0, green: 157.0/255.0, blue: 173.0/255.0, alpha: 1.0)
  57. default:
  58. // Asset.Colors.Label.primary.color
  59. button.selectedTintColor = UIColor(red: 40.0/255.0, green: 44.0/255.0, blue: 55.0/255.0, alpha: 1.0)
  60. // Asset.Colors.Label.secondary.color
  61. button.tintColor = UIColor(red: 60.0/255.0, green: 60.0/255.0, blue: 67.0/255.0, alpha: 0.6)
  62. }
  63. button.backgroundColor = .clear
  64. button.contentInset = UIEdgeInsets(top: 12, left: 26, bottom: 12, right: 26)
  65. }
  66. }
  67. }
  68. extension DiscoveryViewController {
  69. public override func viewDidLoad() {
  70. super.viewDidLoad()
  71. setupAppearance(theme: ThemeService.shared.currentTheme.value)
  72. ThemeService.shared.currentTheme
  73. .receive(on: DispatchQueue.main)
  74. .sink { [weak self] theme in
  75. guard let self = self else { return }
  76. self.setupAppearance(theme: theme)
  77. }
  78. .store(in: &disposeBag)
  79. dataSource = viewModel
  80. addBar(
  81. buttonBar,
  82. dataSource: viewModel,
  83. at: .top
  84. )
  85. customizeButtonBarAppearance()
  86. viewModel.$viewControllers
  87. .receive(on: DispatchQueue.main)
  88. .sink { [weak self] _ in
  89. guard let self = self else { return }
  90. self.reloadData()
  91. }
  92. .store(in: &disposeBag)
  93. }
  94. public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
  95. super.traitCollectionDidChange(previousTraitCollection)
  96. customizeButtonBarAppearance()
  97. }
  98. }
  99. extension DiscoveryViewController {
  100. private func setupAppearance(theme: Theme) {
  101. view.backgroundColor = theme.secondarySystemBackgroundColor
  102. buttonBarBackgroundView.backgroundColor = theme.systemBackgroundColor
  103. }
  104. }
  105. // MARK: - ScrollViewContainer
  106. extension DiscoveryViewController: ScrollViewContainer {
  107. var scrollView: UIScrollView {
  108. return (currentViewController as? ScrollViewContainer)?.scrollView ?? UIScrollView()
  109. }
  110. func scrollToTop(animated: Bool) {
  111. if scrollView.contentOffset.y <= 0 {
  112. scrollToPage(.first, animated: animated)
  113. } else {
  114. scrollView.scrollToTop(animated: animated)
  115. }
  116. }
  117. }
  118. extension DiscoveryViewController {
  119. public override var keyCommands: [UIKeyCommand]? {
  120. return pageboyNavigateKeyCommands
  121. }
  122. }
  123. // MARK: - PageboyNavigateable
  124. extension DiscoveryViewController: PageboyNavigateable {
  125. var navigateablePageViewController: PageboyViewController {
  126. return self
  127. }
  128. @objc func pageboyNavigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
  129. pageboyNavigateKeyCommandHandler(sender)
  130. }
  131. }