feat: add account switcher for iPad sidebar
This commit is contained in:
parent
0ec20c6c88
commit
bcddcf226b
|
@ -7,12 +7,12 @@
|
|||
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>42</integer>
|
||||
<integer>35</integer>
|
||||
</dict>
|
||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>43</integer>
|
||||
<integer>36</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -97,7 +97,7 @@
|
|||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>44</integer>
|
||||
<integer>37</integer>
|
||||
</dict>
|
||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -117,7 +117,7 @@
|
|||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>41</integer>
|
||||
<integer>38</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -139,6 +139,7 @@ extension SceneCoordinator {
|
|||
case show // push
|
||||
case showDetail // replace
|
||||
case modal(animated: Bool, completion: (() -> Void)? = nil)
|
||||
case popover(sourceView: UIView)
|
||||
case panModal
|
||||
case custom(transitioningDelegate: UIViewControllerTransitioningDelegate)
|
||||
case customPush
|
||||
|
@ -326,7 +327,10 @@ extension SceneCoordinator {
|
|||
panModalPresentable.transitioningDelegate = PanModalPresentationDelegate.default
|
||||
presentingViewController.present(panModalPresentable, animated: true, completion: nil)
|
||||
//presentingViewController.presentPanModal(panModalPresentable)
|
||||
|
||||
case .popover(let sourceView):
|
||||
viewController.modalPresentationStyle = .popover
|
||||
viewController.popoverPresentationController?.sourceView = sourceView
|
||||
(splitViewController ?? presentingViewController)?.present(viewController, animated: true, completion: nil)
|
||||
case .custom(let transitioningDelegate):
|
||||
viewController.modalPresentationStyle = .custom
|
||||
viewController.transitioningDelegate = transitioningDelegate
|
||||
|
|
|
@ -9,7 +9,7 @@ import UIKit
|
|||
import MetaTextKit
|
||||
|
||||
final class AddAccountTableViewCell: UITableViewCell {
|
||||
|
||||
|
||||
let iconImageView: UIImageView = {
|
||||
let image = UIImage(systemName: "plus.circle.fill")!
|
||||
let imageView = UIImageView(image: image)
|
||||
|
@ -51,6 +51,28 @@ extension AddAccountTableViewCell {
|
|||
])
|
||||
iconImageView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
iconImageView.setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||
|
||||
// layout the same placeholder UI from `AccountListTableViewCell`
|
||||
let placeholderLabelContainerStackView = UIStackView()
|
||||
placeholderLabelContainerStackView.axis = .vertical
|
||||
placeholderLabelContainerStackView.distribution = .equalCentering
|
||||
placeholderLabelContainerStackView.spacing = 2
|
||||
placeholderLabelContainerStackView.distribution = .fillProportionally
|
||||
placeholderLabelContainerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(placeholderLabelContainerStackView)
|
||||
NSLayoutConstraint.activate([
|
||||
placeholderLabelContainerStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
|
||||
placeholderLabelContainerStackView.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: 10),
|
||||
contentView.bottomAnchor.constraint(equalTo: placeholderLabelContainerStackView.bottomAnchor, constant: 10),
|
||||
iconImageView.heightAnchor.constraint(equalTo: placeholderLabelContainerStackView.heightAnchor, multiplier: 0.8).priority(.required - 10),
|
||||
])
|
||||
let _nameLabel = MetaLabel(style: .accountListName)
|
||||
_nameLabel.configure(content: PlaintextMetaContent(string: " "))
|
||||
let _usernameLabel = MetaLabel(style: .accountListUsername)
|
||||
_usernameLabel.configure(content: PlaintextMetaContent(string: " "))
|
||||
placeholderLabelContainerStackView.addArrangedSubview(_nameLabel)
|
||||
placeholderLabelContainerStackView.addArrangedSubview(_usernameLabel)
|
||||
placeholderLabelContainerStackView.isHidden = true
|
||||
|
||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(titleLabel)
|
||||
|
@ -58,7 +80,7 @@ extension AddAccountTableViewCell {
|
|||
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15),
|
||||
titleLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: 10),
|
||||
contentView.bottomAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 15),
|
||||
iconImageView.heightAnchor.constraint(equalTo: titleLabel.heightAnchor, multiplier: 1.0).priority(.required - 10),
|
||||
// iconImageView.heightAnchor.constraint(equalTo: titleLabel.heightAnchor, multiplier: 1.0).priority(.required - 10),
|
||||
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
])
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ extension ContentSplitViewController {
|
|||
sidebarViewController.didMove(toParent: self)
|
||||
NSLayoutConstraint.activate([
|
||||
mainTabBarController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
mainTabBarController.view.leadingAnchor.constraint(equalTo: sidebarViewController.view.trailingAnchor),
|
||||
mainTabBarController.view.leadingAnchor.constraint(equalTo: sidebarViewController.view.trailingAnchor, constant: UIView.separatorLineHeight(of: view)),
|
||||
mainTabBarController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
mainTabBarController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
|
@ -87,6 +87,22 @@ extension ContentSplitViewController: SidebarViewControllerDelegate {
|
|||
assertionFailure()
|
||||
return
|
||||
}
|
||||
let previousTab = currentSupplementaryTab
|
||||
currentSupplementaryTab = tab
|
||||
|
||||
if previousTab == tab,
|
||||
let navigationController = mainTabBarController.selectedViewController as? UINavigationController
|
||||
{
|
||||
navigationController.popToRootViewController(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
func sidebarViewController(_ sidebarViewController: SidebarViewController, didLongPressItem item: SidebarViewModel.Item, sourceView: UIView) {
|
||||
guard case let .tab(tab) = item, tab == .me else { return }
|
||||
|
||||
let accountListViewController = coordinator.present(scene: .accountList, from: nil, transition: .popover(sourceView: sourceView)) as! AccountListViewController
|
||||
accountListViewController.dragIndicatorView.barView.isHidden = true
|
||||
accountListViewController.preferredContentSize = CGSize(width: 300, height: 320)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,8 +26,17 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency {
|
|||
return contentSplitViewController
|
||||
}()
|
||||
|
||||
private(set) lazy var searchViewController: SearchViewController = {
|
||||
let searchViewController = SearchViewController()
|
||||
searchViewController.context = context
|
||||
searchViewController.coordinator = coordinator
|
||||
return searchViewController
|
||||
}()
|
||||
|
||||
lazy var compactMainTabBarViewController = MainTabBarController(context: context, coordinator: coordinator)
|
||||
|
||||
let separatorLine = UIView.separatorLine
|
||||
|
||||
init(context: AppContext, coordinator: SceneCoordinator) {
|
||||
self.context = context
|
||||
self.coordinator = coordinator
|
||||
|
@ -48,13 +57,9 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency {
|
|||
// Fallback on earlier versions
|
||||
}
|
||||
|
||||
setViewController(UIViewController(), for: .primary)
|
||||
setViewController(searchViewController, for: .primary)
|
||||
setViewController(contentSplitViewController, for: .secondary)
|
||||
setViewController(compactMainTabBarViewController, for: .compact)
|
||||
|
||||
contentSplitViewController.sidebarViewController.view.layer.zPosition = 100
|
||||
contentSplitViewController.mainTabBarController.view.layer.zPosition = 90
|
||||
view.layer.zPosition = 80
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
@ -80,6 +85,15 @@ extension RootSplitViewController {
|
|||
self.updateBehavior(size: self.view.frame.size)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
setupBackground(theme: ThemeService.shared.currentTheme.value)
|
||||
ThemeService.shared.currentTheme
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] theme in
|
||||
guard let self = self else { return }
|
||||
self.setupBackground(theme: theme)
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
|
@ -108,6 +122,15 @@ extension RootSplitViewController {
|
|||
|
||||
}
|
||||
|
||||
extension RootSplitViewController {
|
||||
|
||||
private func setupBackground(theme: Theme) {
|
||||
// this set column separator line color
|
||||
view.backgroundColor = theme.separator
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UISplitViewControllerDelegate
|
||||
extension RootSplitViewController: UISplitViewControllerDelegate {
|
||||
|
||||
|
|
|
@ -12,10 +12,13 @@ import CoreDataStack
|
|||
|
||||
protocol SidebarViewControllerDelegate: AnyObject {
|
||||
func sidebarViewController(_ sidebarViewController: SidebarViewController, didSelectTab tab: MainTabBarController.Tab)
|
||||
func sidebarViewController(_ sidebarViewController: SidebarViewController, didLongPressItem item: SidebarViewModel.Item, sourceView: UIView)
|
||||
}
|
||||
|
||||
final class SidebarViewController: UIViewController, NeedsDependency {
|
||||
|
||||
let logger = Logger(subsystem: "SidebarViewController", category: "ViewController")
|
||||
|
||||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
|
@ -127,6 +130,10 @@ extension SidebarViewController {
|
|||
self.collectionView.contentInset.bottom = height
|
||||
}
|
||||
.store(in: &observations)
|
||||
|
||||
let sidebarLongPressGestureRecognizer = UILongPressGestureRecognizer()
|
||||
sidebarLongPressGestureRecognizer.addTarget(self, action: #selector(SidebarViewController.sidebarLongPressGestureRecognizerHandler(_:)))
|
||||
collectionView.addGestureRecognizer(sidebarLongPressGestureRecognizer)
|
||||
}
|
||||
|
||||
private func setupBackground(theme: Theme) {
|
||||
|
@ -148,6 +155,23 @@ extension SidebarViewController {
|
|||
|
||||
}
|
||||
|
||||
extension SidebarViewController {
|
||||
@objc private func sidebarLongPressGestureRecognizerHandler(_ sender: UILongPressGestureRecognizer) {
|
||||
guard sender.state == .began else { return }
|
||||
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
assert(sender.view === collectionView)
|
||||
|
||||
let position = sender.location(in: collectionView)
|
||||
guard let indexPath = collectionView.indexPathForItem(at: position) else { return }
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||
guard let cell = collectionView.cellForItem(at: indexPath) else { return }
|
||||
delegate?.sidebarViewController(self, didLongPressItem: item, sourceView: cell)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
extension SidebarViewController: UICollectionViewDelegate {
|
||||
|
||||
|
@ -179,25 +203,5 @@ extension SidebarViewController: UICollectionViewDelegate {
|
|||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
// switch item {
|
||||
// case .tab(let tab):
|
||||
// delegate?.sidebarViewController(self, didSelectTab: tab)
|
||||
// case .searchHistory(let viewModel):
|
||||
// delegate?.sidebarViewController(self, didSelectSearchHistory: viewModel)
|
||||
// case .header:
|
||||
// break
|
||||
// case .account(let viewModel):
|
||||
// assert(Thread.isMainThread)
|
||||
// let authentication = context.managedObjectContext.object(with: viewModel.authenticationObjectID) as! MastodonAuthentication
|
||||
// context.authenticationService.activeMastodonUser(domain: authentication.domain, userID: authentication.userID)
|
||||
// .receive(on: DispatchQueue.main)
|
||||
// .sink { [weak self] result in
|
||||
// guard let self = self else { return }
|
||||
// self.coordinator.setup()
|
||||
// }
|
||||
// .store(in: &disposeBag)
|
||||
// case .addAccount:
|
||||
// coordinator.present(scene: .welcome, from: self, transition: .modal(animated: true, completion: nil))
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ final class SidebarListContentView: UIView, UIContentView {
|
|||
let avatarButton: CircleAvatarButton = {
|
||||
let button = CircleAvatarButton()
|
||||
button.borderWidth = 2
|
||||
button.borderColor = UIColor.label.cgColor
|
||||
return button
|
||||
}()
|
||||
|
||||
|
@ -71,6 +72,9 @@ extension SidebarListContentView {
|
|||
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
avatarButton.contentMode = .scaleAspectFit
|
||||
|
||||
imageView.isUserInteractionEnabled = false
|
||||
avatarButton.isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
private func apply(configuration: ContentConfiguration) {
|
||||
|
|
Loading…
Reference in New Issue