chore: update onboarding layout for iPad

This commit is contained in:
CMK 2021-10-08 18:10:06 +08:00
parent bcf0262608
commit 6b1d3f8738
15 changed files with 448 additions and 116 deletions

View File

@ -7,12 +7,12 @@
<key>AppShared.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>56</integer>
<integer>35</integer>
</dict>
<key>CoreDataStack.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>54</integer>
<integer>38</integer>
</dict>
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
<dict>
@ -97,7 +97,7 @@
<key>MastodonIntent.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>51</integer>
<integer>36</integer>
</dict>
<key>MastodonIntents.xcscheme_^#shared#^_</key>
<dict>
@ -117,7 +117,7 @@
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>55</integer>
<integer>37</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -214,7 +214,13 @@ extension SceneCoordinator {
assertionFailure()
return nil
}
presentingViewController.presentPanModal(panModalPresentable)
// https://github.com/slackhq/PanModal/issues/74#issuecomment-572426441
panModalPresentable.modalPresentationStyle = .custom
panModalPresentable.modalPresentationCapturesStatusBarAppearance = true
panModalPresentable.transitioningDelegate = PanModalPresentationDelegate.default
presentingViewController.present(panModalPresentable, animated: true, completion: nil)
//presentingViewController.presentPanModal(panModalPresentable)
case .custom(let transitioningDelegate):
viewController.modalPresentationStyle = .custom

View File

@ -20,6 +20,8 @@ final class MastodonConfirmEmailViewController: UIViewController, NeedsDependenc
var viewModel: MastodonConfirmEmailViewModel!
let stackView = UIStackView()
let largeTitleLabel: UILabel = {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: UIFont.systemFont(ofSize: 34, weight: .bold))
@ -72,9 +74,10 @@ extension MastodonConfirmEmailViewController {
override func viewDidLoad() {
setupOnboardingAppearance()
configureTitleLabel()
configureMargin()
// stackView
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
@ -92,8 +95,8 @@ extension MastodonConfirmEmailViewController {
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor),
stackView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
stackView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor),
])
NSLayoutConstraint.activate([
@ -139,6 +142,38 @@ extension MastodonConfirmEmailViewController {
.store(in: &self.disposeBag)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureTitleLabel()
configureMargin()
}
}
extension MastodonConfirmEmailViewController {
private func configureTitleLabel() {
switch traitCollection.horizontalSizeClass {
case .regular:
navigationItem.largeTitleDisplayMode = .always
navigationItem.title = L10n.Scene.ConfirmEmail.title.replacingOccurrences(of: "\n", with: " ")
largeTitleLabel.isHidden = true
default:
navigationItem.largeTitleDisplayMode = .never
navigationItem.title = nil
largeTitleLabel.isHidden = false
}
}
private func configureMargin() {
switch traitCollection.horizontalSizeClass {
case .regular:
let margin = MastodonConfirmEmailViewController.viewEdgeMargin
stackView.layoutMargins = UIEdgeInsets(top: 18, left: margin, bottom: 23, right: margin)
default:
stackView.layoutMargins = UIEdgeInsets(top: 10, left: 0, bottom: 23, right: 0)
}
}
}
extension MastodonConfirmEmailViewController {

View File

@ -29,6 +29,8 @@ final class MastodonPickServerViewController: UIViewController, NeedsDependency
private var expandServerDomainSet = Set<String>()
private let emptyStateView = PickServerEmptyStateView()
private var emptyStateViewLeadingLayoutConstraint: NSLayoutConstraint!
private var emptyStateViewTrailingLayoutConstraint: NSLayoutConstraint!
let tableViewTopPaddingView = UIView() // fix empty state view background display when tableView bounce scrolling
var tableViewTopPaddingViewHeightLayoutConstraint: NSLayoutConstraint!
@ -52,13 +54,14 @@ final class MastodonPickServerViewController: UIViewController, NeedsDependency
return tableView
}()
let buttonContainer = UIView()
let nextStepButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
var nextStepButtonBottomLayoutConstraint: NSLayoutConstraint!
var buttonContainerBottomLayoutConstraint: NSLayoutConstraint!
var mastodonAuthenticationController: MastodonAuthenticationController?
@ -77,6 +80,8 @@ extension MastodonPickServerViewController {
setupOnboardingAppearance()
defer { setupNavigationBarBackgroundView() }
configureTitleLabel()
configureMargin()
#if DEBUG
navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), style: .plain, target: nil, action: nil)
@ -89,14 +94,24 @@ extension MastodonPickServerViewController {
navigationItem.rightBarButtonItem?.menu = UIMenu(title: "Debug Tool", image: nil, identifier: nil, options: [], children: children)
#endif
view.addSubview(nextStepButton)
nextStepButtonBottomLayoutConstraint = view.bottomAnchor.constraint(equalTo: nextStepButton.bottomAnchor, constant: 0).priority(.defaultHigh)
buttonContainer.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.preservesSuperviewLayoutMargins = true
view.addSubview(buttonContainer)
buttonContainerBottomLayoutConstraint = view.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor, constant: 0).priority(.defaultHigh)
NSLayoutConstraint.activate([
nextStepButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: MastodonPickServerViewController.actionButtonMargin),
view.readableContentGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor, constant: MastodonPickServerViewController.actionButtonMargin),
buttonContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor),
buttonContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor),
view.safeAreaLayoutGuide.bottomAnchor.constraint(greaterThanOrEqualTo: buttonContainer.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight),
buttonContainerBottomLayoutConstraint,
])
view.addSubview(nextStepButton)
NSLayoutConstraint.activate([
nextStepButton.topAnchor.constraint(equalTo: buttonContainer.topAnchor),
nextStepButton.leadingAnchor.constraint(equalTo: buttonContainer.layoutMarginsGuide.leadingAnchor),
buttonContainer.layoutMarginsGuide.trailingAnchor.constraint(equalTo: nextStepButton.trailingAnchor),
nextStepButton.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor),
nextStepButton.heightAnchor.constraint(equalToConstant: MastodonPickServerViewController.actionButtonHeight).priority(.defaultHigh),
view.safeAreaLayoutGuide.bottomAnchor.constraint(greaterThanOrEqualTo: nextStepButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight),
nextStepButtonBottomLayoutConstraint,
])
// fix AutoLayout warning when observe before view appear
@ -127,16 +142,18 @@ extension MastodonPickServerViewController {
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
nextStepButton.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 7),
buttonContainer.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 7),
])
emptyStateView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(emptyStateView)
emptyStateViewLeadingLayoutConstraint = emptyStateView.leadingAnchor.constraint(equalTo: tableView.leadingAnchor)
emptyStateViewTrailingLayoutConstraint = tableView.trailingAnchor.constraint(equalTo: emptyStateView.trailingAnchor)
NSLayoutConstraint.activate([
emptyStateView.topAnchor.constraint(equalTo: view.topAnchor),
emptyStateView.leadingAnchor.constraint(equalTo: tableView.readableContentGuide.leadingAnchor),
emptyStateView.trailingAnchor.constraint(equalTo: tableView.readableContentGuide.trailingAnchor),
nextStepButton.topAnchor.constraint(equalTo: emptyStateView.bottomAnchor, constant: 21),
emptyStateViewLeadingLayoutConstraint,
emptyStateViewTrailingLayoutConstraint,
buttonContainer.topAnchor.constraint(equalTo: emptyStateView.bottomAnchor, constant: 21),
])
view.sendSubviewToBack(emptyStateView)
@ -154,18 +171,18 @@ extension MastodonPickServerViewController {
// guard external keyboard connected
guard isShow, state == .dock, GCKeyboard.coalesced != nil else {
self.nextStepButtonBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight
self.buttonContainerBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight
return
}
let externalKeyboardToolbarHeight = self.view.frame.maxY - endFrame.minY
guard externalKeyboardToolbarHeight > 0 else {
self.nextStepButtonBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight
self.buttonContainerBottomLayoutConstraint.constant = WelcomeViewController.viewBottomPaddingHeight
return
}
UIView.animate(withDuration: 0.3) {
self.nextStepButtonBottomLayoutConstraint.constant = externalKeyboardToolbarHeight + 16
self.buttonContainerBottomLayoutConstraint.constant = externalKeyboardToolbarHeight + 16
self.view.layoutIfNeeded()
}
}
@ -274,9 +291,34 @@ extension MastodonPickServerViewController {
viewModel.viewWillAppear.send()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
setupNavigationBarAppearance()
updateEmptyStateViewLayout()
configureTitleLabel()
configureMargin()
}
}
extension MastodonPickServerViewController {
private func configureTitleLabel() {
guard UIDevice.current.userInterfaceIdiom == .pad else {
return
}
switch traitCollection.horizontalSizeClass {
case .regular:
navigationItem.largeTitleDisplayMode = .always
navigationItem.title = L10n.Scene.ServerPicker.title.replacingOccurrences(of: "\n", with: " ")
default:
navigationItem.largeTitleDisplayMode = .never
navigationItem.title = nil
}
}
}
extension MastodonPickServerViewController {
@objc
@ -426,43 +468,6 @@ extension MastodonPickServerViewController: UITableViewDelegate {
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
guard let diffableDataSource = viewModel.diffableDataSource else {
return .leastNonzeroMagnitude
}
let sections = diffableDataSource.snapshot().sectionIdentifiers
let section = sections[section]
switch section {
case .header:
return 20
case .category:
// Since category view has a blur shadow effect, its height need to be large than the actual height,
// Thus we reduce the section header's height by 10, and make the category cell height 60+20(10 inset for top and bottom)
return 10
case .search:
// Same reason as above
return 10
case .servers:
return .leastNonzeroMagnitude
}
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView()
footerView.backgroundColor = .yellow
return footerView
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return .leastNonzeroMagnitude
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return nil }
@ -521,6 +526,26 @@ extension MastodonPickServerViewController {
let rectInTableView = tableView.rectForRow(at: indexPath)
emptyStateView.topPaddingViewTopLayoutConstraint.constant = rectInTableView.maxY
switch traitCollection.horizontalSizeClass {
case .regular:
emptyStateViewLeadingLayoutConstraint.constant = MastodonPickServerViewController.viewEdgeMargin
emptyStateViewTrailingLayoutConstraint.constant = MastodonPickServerViewController.viewEdgeMargin
default:
let margin = tableView.layoutMarginsGuide.layoutFrame.origin.x
emptyStateViewLeadingLayoutConstraint.constant = margin
emptyStateViewTrailingLayoutConstraint.constant = margin
}
}
private func configureMargin() {
switch traitCollection.horizontalSizeClass {
case .regular:
let margin = MastodonPickServerViewController.viewEdgeMargin
buttonContainer.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin)
default:
buttonContainer.layoutMargins = .zero
}
}
}

View File

@ -56,12 +56,13 @@ extension PickServerCategoriesCell {
private func _init() {
selectionStyle = .none
backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
configureMargin()
metricView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(metricView)
NSLayoutConstraint.activate([
metricView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
metricView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
metricView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
metricView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
metricView.topAnchor.constraint(equalTo: contentView.topAnchor),
metricView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
metricView.heightAnchor.constraint(equalToConstant: 80).priority(.defaultHigh),
@ -71,14 +72,20 @@ extension PickServerCategoriesCell {
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
contentView.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 20),
collectionView.heightAnchor.constraint(equalToConstant: 80).priority(.defaultHigh),
])
collectionView.delegate = self
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureMargin()
}
override func layoutSubviews() {
super.layoutSubviews()
@ -87,6 +94,18 @@ extension PickServerCategoriesCell {
}
extension PickServerCategoriesCell {
private func configureMargin() {
switch traitCollection.horizontalSizeClass {
case .regular:
let margin = MastodonPickServerViewController.viewEdgeMargin
contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin)
default:
contentView.layoutMargins = .zero
}
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension PickServerCategoriesCell: UICollectionViewDelegateFlowLayout {

View File

@ -198,6 +198,7 @@ extension PickServerCell {
private func _init() {
selectionStyle = .none
backgroundColor = .clear
configureMargin()
contentView.addSubview(containerView)
containerView.addSubview(domainLabel)
@ -229,8 +230,8 @@ extension PickServerCell {
NSLayoutConstraint.activate([
// Set background view
containerView.topAnchor.constraint(equalTo: contentView.topAnchor),
containerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
// Set bottom separator
@ -291,6 +292,12 @@ extension PickServerCell {
expandButton.addTarget(self, action: #selector(expandButtonDidPressed(_:)), for: .touchUpInside)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureMargin()
}
private func makeVerticalInfoStackView(arrangedView: UIView...) -> UIStackView {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
@ -318,6 +325,18 @@ extension PickServerCell {
}
}
extension PickServerCell {
private func configureMargin() {
switch traitCollection.horizontalSizeClass {
case .regular:
let margin = MastodonPickServerViewController.viewEdgeMargin
contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin)
default:
contentView.layoutMargins = .zero
}
}
}
extension PickServerCell {
enum ExpandMode {

View File

@ -37,14 +37,16 @@ final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell {
override func _init() {
super._init()
configureMargin()
contentView.addSubview(containerView)
contentView.addSubview(seperator)
NSLayoutConstraint.activate([
// Set background view
containerView.topAnchor.constraint(equalTo: contentView.topAnchor),
containerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 1),
// Set bottom separator
@ -67,6 +69,24 @@ final class PickServerLoaderTableViewCell: TimelineLoaderTableViewCell {
activityIndicatorView.isHidden = false
startAnimating()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureMargin()
}
}
extension PickServerLoaderTableViewCell {
private func configureMargin() {
switch traitCollection.horizontalSizeClass {
case .regular:
let margin = MastodonPickServerViewController.viewEdgeMargin
contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin)
default:
contentView.layoutMargins = .zero
}
}
}
#if canImport(SwiftUI) && DEBUG

View File

@ -109,6 +109,7 @@ extension PickServerSearchCell {
private func _init() {
selectionStyle = .none
backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
configureMargin()
searchTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
searchTextField.delegate = self
@ -118,9 +119,9 @@ extension PickServerSearchCell {
contentView.addSubview(searchTextField)
NSLayoutConstraint.activate([
bgView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
bgView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
bgView.topAnchor.constraint(equalTo: contentView.topAnchor),
bgView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
bgView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
bgView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
textFieldBgView.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 14),
@ -134,6 +135,24 @@ extension PickServerSearchCell {
textFieldBgView.bottomAnchor.constraint(equalTo: searchTextField.bottomAnchor, constant: 4),
])
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureMargin()
}
}
extension PickServerSearchCell {
private func configureMargin() {
switch traitCollection.horizontalSizeClass {
case .regular:
let margin = MastodonPickServerViewController.viewEdgeMargin
contentView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin)
default:
contentView.layoutMargins = .zero
}
}
}
extension PickServerSearchCell {

View File

@ -20,6 +20,8 @@ final class PickServerTitleCell: UITableViewCell {
return label
}()
var containerHeightLayoutConstraint: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
_init()
@ -36,13 +38,45 @@ extension PickServerTitleCell {
private func _init() {
selectionStyle = .none
backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
contentView.addSubview(titleLabel)
let container = UIStackView()
container.axis = .vertical
container.translatesAutoresizingMaskIntoConstraints = false
containerHeightLayoutConstraint = container.heightAnchor.constraint(equalToConstant: .leastNonzeroMagnitude)
contentView.addSubview(container)
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
contentView.readableContentGuide.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
container.topAnchor.constraint(equalTo: contentView.topAnchor),
container.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
container.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
container.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
])
container.addArrangedSubview(titleLabel)
configureTitleLabelDisplay()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureTitleLabelDisplay()
}
}
extension PickServerTitleCell {
private func configureTitleLabelDisplay() {
guard traitCollection.userInterfaceIdiom == .pad else {
titleLabel.isHidden = false
return
}
switch traitCollection.horizontalSizeClass {
case .regular:
titleLabel.isHidden = true
containerHeightLayoutConstraint.isActive = true
default:
titleLabel.isHidden = false
containerHeightLayoutConstraint.isActive = false
}
}
}

View File

@ -61,6 +61,8 @@ final class MastodonRegisterViewController: UIViewController, NeedsDependency, O
return scrollview
}()
let stackView = UIStackView()
let largeTitleLabel: UILabel = {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold))
@ -287,7 +289,11 @@ extension MastodonRegisterViewController {
super.viewDidLoad()
setupOnboardingAppearance()
defer { setupNavigationBarBackgroundView() }
configureTitleLabel()
defer {
setupNavigationBarBackgroundView()
configureFormLayout()
}
avatarButton.menu = createMediaContextMenu()
avatarButton.showsMenuAsPrimaryAction = true
@ -307,7 +313,6 @@ extension MastodonRegisterViewController {
tapGestureRecognizer.addTarget(self, action: #selector(tapGestureRecognizerHandler))
// stackview
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 40
@ -315,17 +320,24 @@ extension MastodonRegisterViewController {
stackView.isLayoutMarginsRelativeArrangement = true
stackView.addArrangedSubview(largeTitleLabel)
stackView.addArrangedSubview(avatarView)
stackView.addArrangedSubview(usernameTextField)
stackView.addArrangedSubview(displayNameTextField)
stackView.addArrangedSubview(emailTextField)
stackView.addArrangedSubview(passwordTextField)
stackView.addArrangedSubview(passwordCheckLabel)
let formTableStackView = UIStackView()
stackView.addArrangedSubview(formTableStackView)
formTableStackView.axis = .vertical
formTableStackView.distribution = .fill
formTableStackView.spacing = 40
formTableStackView.addArrangedSubview(usernameTextField)
formTableStackView.addArrangedSubview(displayNameTextField)
formTableStackView.addArrangedSubview(emailTextField)
formTableStackView.addArrangedSubview(passwordTextField)
formTableStackView.addArrangedSubview(passwordCheckLabel)
if viewModel.approvalRequired {
stackView.addArrangedSubview(reasonTextField)
formTableStackView.addArrangedSubview(reasonTextField)
}
usernameErrorPromptLabel.translatesAutoresizingMaskIntoConstraints = false
stackView.addSubview(usernameErrorPromptLabel)
formTableStackView.addSubview(usernameErrorPromptLabel)
NSLayoutConstraint.activate([
usernameErrorPromptLabel.topAnchor.constraint(equalTo: usernameTextField.bottomAnchor, constant: 6),
usernameErrorPromptLabel.leadingAnchor.constraint(equalTo: usernameTextField.leadingAnchor),
@ -333,7 +345,7 @@ extension MastodonRegisterViewController {
])
emailErrorPromptLabel.translatesAutoresizingMaskIntoConstraints = false
stackView.addSubview(emailErrorPromptLabel)
formTableStackView.addSubview(emailErrorPromptLabel)
NSLayoutConstraint.activate([
emailErrorPromptLabel.topAnchor.constraint(equalTo: emailTextField.bottomAnchor, constant: 6),
emailErrorPromptLabel.leadingAnchor.constraint(equalTo: emailTextField.leadingAnchor),
@ -341,7 +353,7 @@ extension MastodonRegisterViewController {
])
passwordErrorPromptLabel.translatesAutoresizingMaskIntoConstraints = false
stackView.addSubview(passwordErrorPromptLabel)
formTableStackView.addSubview(passwordErrorPromptLabel)
NSLayoutConstraint.activate([
passwordErrorPromptLabel.topAnchor.constraint(equalTo: passwordCheckLabel.bottomAnchor, constant: 2),
passwordErrorPromptLabel.leadingAnchor.constraint(equalTo: passwordTextField.leadingAnchor),
@ -373,12 +385,14 @@ extension MastodonRegisterViewController {
avatarView.translatesAutoresizingMaskIntoConstraints = false
avatarView.addSubview(avatarButton)
NSLayoutConstraint.activate([
avatarView.heightAnchor.constraint(equalToConstant: 90).priority(.defaultHigh),
avatarView.heightAnchor.constraint(equalToConstant: 92).priority(.required - 1),
])
avatarButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
avatarButton.heightAnchor.constraint(equalToConstant: 92).priority(.defaultHigh),
avatarButton.widthAnchor.constraint(equalToConstant: 92).priority(.defaultHigh),
avatarButton.heightAnchor.constraint(equalToConstant: 92).priority(.required - 1),
avatarButton.widthAnchor.constraint(equalToConstant: 92).priority(.required - 1),
avatarButton.leadingAnchor.constraint(greaterThanOrEqualTo: avatarView.leadingAnchor).priority(.required - 1),
avatarView.trailingAnchor.constraint(greaterThanOrEqualTo: avatarButton.trailingAnchor).priority(.required - 1),
avatarButton.centerXAnchor.constraint(equalTo: avatarView.centerXAnchor),
avatarButton.centerYAnchor.constraint(equalTo: avatarView.centerYAnchor),
])
@ -392,15 +406,15 @@ extension MastodonRegisterViewController {
// textfield
NSLayoutConstraint.activate([
usernameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh),
displayNameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh),
emailTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh),
passwordTextField.heightAnchor.constraint(equalToConstant: 50).priority(.defaultHigh),
usernameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1),
displayNameTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1),
emailTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1),
passwordTextField.heightAnchor.constraint(equalToConstant: 50).priority(.required - 1),
])
// password
stackView.setCustomSpacing(6, after: passwordTextField)
stackView.setCustomSpacing(32, after: passwordCheckLabel)
formTableStackView.setCustomSpacing(6, after: passwordTextField)
formTableStackView.setCustomSpacing(32, after: passwordCheckLabel)
// return
if viewModel.approvalRequired {
@ -410,16 +424,22 @@ extension MastodonRegisterViewController {
}
// button
stackView.addArrangedSubview(buttonContainer)
formTableStackView.addArrangedSubview(buttonContainer)
signUpButton.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addSubview(signUpButton)
NSLayoutConstraint.activate([
signUpButton.topAnchor.constraint(equalTo: buttonContainer.topAnchor),
signUpButton.leadingAnchor.constraint(equalTo: buttonContainer.leadingAnchor, constant: MastodonRegisterViewController.actionButtonMargin),
buttonContainer.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: MastodonRegisterViewController.actionButtonMargin),
signUpButton.leadingAnchor.constraint(equalTo: buttonContainer.leadingAnchor),
buttonContainer.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor),
buttonContainer.bottomAnchor.constraint(equalTo: signUpButton.bottomAnchor),
signUpButton.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.defaultHigh),
signUpButton.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.required - 1),
buttonContainer.heightAnchor.constraint(equalToConstant: MastodonRegisterViewController.actionButtonHeight).priority(.required - 1),
])
signUpButton.setContentHuggingPriority(.defaultLow, for: .horizontal)
signUpButton.setContentHuggingPriority(.defaultLow, for: .vertical)
signUpButton.setContentCompressionResistancePriority(.required - 1, for: .vertical)
signUpButton.setContentCompressionResistancePriority(.required - 1, for: .horizontal)
buttonContainer.setContentCompressionResistancePriority(.required - 1, for: .vertical)
Publishers.CombineLatest(
KeyboardResponderService.shared.state.eraseToAnyPublisher(),
@ -645,6 +665,12 @@ extension MastodonRegisterViewController {
plusIconImageView.layer.masksToBounds = true
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureTitleLabel()
configureFormLayout()
}
}
extension MastodonRegisterViewController: UITextFieldDelegate {
@ -714,7 +740,7 @@ extension MastodonRegisterViewController: UITextFieldDelegate {
textField.layer.shadowRadius = 2.0
textField.layer.shadowOffset = CGSize.zero
textField.layer.shadowColor = color.cgColor
textField.layer.shadowPath = UIBezierPath(roundedRect: textField.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)).cgPath
// textField.layer.shadowPath = UIBezierPath(roundedRect: textField.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 2.0, height: 2.0)).cgPath
}
private func setTextFieldValidAppearance(_ textField: UITextField, validateState: MastodonRegisterViewModel.ValidateState) {
@ -729,6 +755,36 @@ extension MastodonRegisterViewController: UITextFieldDelegate {
}
}
extension MastodonRegisterViewController {
private func configureTitleLabel() {
switch traitCollection.horizontalSizeClass {
case .regular:
navigationItem.largeTitleDisplayMode = .always
navigationItem.title = L10n.Scene.ServerPicker.title.replacingOccurrences(of: "\n", with: " ")
largeTitleLabel.isHidden = true
default:
navigationItem.largeTitleDisplayMode = .never
navigationItem.title = nil
largeTitleLabel.isHidden = false
}
}
private func configureFormLayout() {
switch traitCollection.horizontalSizeClass {
case .regular:
stackView.axis = .horizontal
stackView.distribution = .fillProportionally
default:
stackView.axis = .vertical
stackView.distribution = .fill
}
}
private func configureMargin() {
}
}
extension MastodonRegisterViewController {
@objc private func tapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
view.endEditing(true)

View File

@ -21,6 +21,8 @@ final class MastodonServerRulesViewController: UIViewController, NeedsDependency
var viewModel: MastodonServerRulesViewModel!
let stackView = UIStackView()
let largeTitleLabel: UILabel = {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold))
@ -96,6 +98,8 @@ extension MastodonServerRulesViewController {
super.viewDidLoad()
setupOnboardingAppearance()
configureTitleLabel()
configureMargin()
configTextView()
defer { setupNavigationBarBackgroundView() }
@ -116,8 +120,8 @@ extension MastodonServerRulesViewController {
bottomContainerView.addSubview(confirmButton)
NSLayoutConstraint.activate([
bottomContainerView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor, constant: MastodonServerRulesViewController.viewBottomPaddingHeight),
confirmButton.leadingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.leadingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin),
bottomContainerView.readableContentGuide.trailingAnchor.constraint(equalTo: confirmButton.trailingAnchor, constant: MastodonServerRulesViewController.actionButtonMargin),
confirmButton.leadingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.leadingAnchor),
bottomContainerView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: confirmButton.trailingAnchor),
confirmButton.heightAnchor.constraint(equalToConstant: MastodonServerRulesViewController.actionButtonHeight).priority(.defaultHigh),
])
@ -125,8 +129,8 @@ extension MastodonServerRulesViewController {
bottomContainerView.addSubview(bottomPromptMetaText.textView)
NSLayoutConstraint.activate([
bottomPromptMetaText.textView.frameLayoutGuide.topAnchor.constraint(equalTo: bottomContainerView.topAnchor, constant: 20),
bottomPromptMetaText.textView.frameLayoutGuide.leadingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.leadingAnchor),
bottomPromptMetaText.textView.frameLayoutGuide.trailingAnchor.constraint(equalTo: bottomContainerView.readableContentGuide.trailingAnchor),
bottomPromptMetaText.textView.frameLayoutGuide.leadingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.leadingAnchor),
bottomPromptMetaText.textView.frameLayoutGuide.trailingAnchor.constraint(equalTo: bottomContainerView.layoutMarginsGuide.trailingAnchor),
confirmButton.topAnchor.constraint(equalTo: bottomPromptMetaText.textView.frameLayoutGuide.bottomAnchor, constant: 20),
])
@ -140,10 +144,10 @@ extension MastodonServerRulesViewController {
scrollView.frameLayoutGuide.widthAnchor.constraint(equalTo: scrollView.contentLayoutGuide.widthAnchor),
])
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
stackView.addArrangedSubview(largeTitleLabel)
stackView.addArrangedSubview(subtitleLabel)
@ -178,6 +182,46 @@ extension MastodonServerRulesViewController {
updateScrollViewContentInset()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
setupNavigationBarAppearance()
configureTitleLabel()
configureMargin()
}
}
extension MastodonServerRulesViewController {
private func configureTitleLabel() {
guard UIDevice.current.userInterfaceIdiom == .pad else {
return
}
switch traitCollection.horizontalSizeClass {
case .regular:
navigationItem.largeTitleDisplayMode = .always
navigationItem.title = L10n.Scene.ServerRules.title.replacingOccurrences(of: "\n", with: " ")
largeTitleLabel.isHidden = true
default:
navigationItem.leftBarButtonItem = nil
navigationItem.largeTitleDisplayMode = .never
navigationItem.title = nil
largeTitleLabel.isHidden = false
}
}
private func configureMargin() {
switch traitCollection.horizontalSizeClass {
case .regular:
let margin = MastodonPickServerViewController.viewEdgeMargin
stackView.layoutMargins = UIEdgeInsets(top: 32, left: margin, bottom: 20, right: margin)
bottomContainerView.layoutMargins = UIEdgeInsets(top: 0, left: margin, bottom: 0, right: margin)
default:
stackView.layoutMargins = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
bottomContainerView.layoutMargins = .zero
}
}
}
extension MastodonServerRulesViewController {

View File

@ -24,19 +24,38 @@ extension OnboardingViewControllerAppearance {
setupNavigationBarAppearance()
let backItem = UIBarButtonItem()
backItem.title = L10n.Common.Controls.Actions.back
let backItem = UIBarButtonItem(
title: L10n.Common.Controls.Actions.back,
style: .plain,
target: nil,
action: nil
)
navigationItem.backBarButtonItem = backItem
}
func setupNavigationBarAppearance() {
// use TransparentBackground so view push / dismiss will be more visual nature
// please add opaque background for status bar manually if needs
let barAppearance = UINavigationBarAppearance()
barAppearance.configureWithTransparentBackground()
navigationController?.navigationBar.standardAppearance = barAppearance
navigationController?.navigationBar.compactAppearance = barAppearance
navigationController?.navigationBar.scrollEdgeAppearance = barAppearance
switch traitCollection.userInterfaceIdiom {
case .pad:
if traitCollection.horizontalSizeClass == .regular {
// do nothing
} else {
fallthrough
}
default:
let barAppearance = UINavigationBarAppearance()
barAppearance.configureWithTransparentBackground()
navigationItem.standardAppearance = barAppearance
navigationItem.compactAppearance = barAppearance
navigationItem.scrollEdgeAppearance = barAppearance
if #available(iOS 15.0, *) {
navigationItem.compactScrollEdgeAppearance = barAppearance
} else {
// Fallback on earlier versions
}
}
}
func setupNavigationBarBackgroundView() {
@ -57,3 +76,12 @@ extension OnboardingViewControllerAppearance {
}
}
extension OnboardingViewControllerAppearance {
static var viewEdgeMargin: CGFloat {
guard UIDevice.current.userInterfaceIdiom == .pad else { return .zero }
let shortEdgeWidth = min(UIScreen.main.bounds.height, UIScreen.main.bounds.width)
return shortEdgeWidth * 0.17 // magic
}
}

View File

@ -75,6 +75,8 @@ extension WelcomeViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .never
view.overrideUserInterfaceStyle = .light
setupOnboardingAppearance()
@ -235,7 +237,21 @@ extension WelcomeViewController {
}
// MARK: - OnboardingViewControllerAppearance
extension WelcomeViewController: OnboardingViewControllerAppearance { }
extension WelcomeViewController: OnboardingViewControllerAppearance {
func setupNavigationBarAppearance() {
// always transparent
let barAppearance = UINavigationBarAppearance()
barAppearance.configureWithTransparentBackground()
navigationItem.standardAppearance = barAppearance
navigationItem.compactAppearance = barAppearance
navigationItem.scrollEdgeAppearance = barAppearance
if #available(iOS 15.0, *) {
navigationItem.compactScrollEdgeAppearance = barAppearance
} else {
// Fallback on earlier versions
}
}
}
// MARK: - UIAdaptivePresentationControllerDelegate
extension WelcomeViewController: UIAdaptivePresentationControllerDelegate {
@ -245,7 +261,12 @@ extension WelcomeViewController: UIAdaptivePresentationControllerDelegate {
// make underneath view controller alive to fix layout issue due to view life cycle
return .fullScreen
default:
return .pageSheet
switch traitCollection.horizontalSizeClass {
case .regular:
return .pageSheet
default:
return .fullScreen
}
}
}

View File

@ -317,7 +317,7 @@ extension MainTabBarController {
switch tab {
case .me:
coordinator.present(scene: .accountList, from: nil, transition: .panModal)
coordinator.present(scene: .accountList, from: self, transition: .panModal)
default:
break
}
@ -353,7 +353,6 @@ extension MainTabBarController {
self.avatarButton.setContentHuggingPriority(.required - 1, for: .vertical)
self.avatarButton.isUserInteractionEnabled = false
}
}
extension MainTabBarController {

View File

@ -201,6 +201,12 @@ extension RootSplitViewController: UISplitViewControllerDelegate {
displayModeForExpandingToProposedDisplayMode proposedDisplayMode: UISplitViewController.DisplayMode
) -> UISplitViewController.DisplayMode {
let compactNavigationController = mainTabBarController.selectedViewController as? UINavigationController
if let topMost = compactNavigationController?.topMost,
topMost is AccountListViewController {
topMost.dismiss(animated: false, completion: nil)
}
let viewControllers = compactNavigationController?.popToRootViewController(animated: true) ?? []
var supplementaryViewControllers: [UIViewController] = []
@ -219,6 +225,7 @@ extension RootSplitViewController: UISplitViewControllerDelegate {
if let secondaryNavigationController = viewController(for: .secondary) as? UINavigationController {
secondaryNavigationController.setViewControllers(secondaryNavigationController.viewControllers + secondaryViewControllers, animated: false)
}
return proposedDisplayMode
}