Remove wizard (IOS-135) (#995)
This commit is contained in:
parent
b7508f8fb6
commit
eb826fb310
|
@ -825,14 +825,8 @@
|
|||
"dismiss_account_switcher": "Dismiss Account Switcher",
|
||||
"add_account": "Add Account"
|
||||
},
|
||||
"wizard": {
|
||||
"new_in_mastodon": "New in Mastodon",
|
||||
"multiple_account_switch_intro_description": "Switch between multiple accounts by holding the profile button.",
|
||||
"accessibility_hint": "Double tap to dismiss this wizard"
|
||||
},
|
||||
"bookmark": {
|
||||
"title": "Bookmarks"
|
||||
|
||||
},
|
||||
"followed_tags": {
|
||||
"title": "Followed Tags",
|
||||
|
|
|
@ -247,7 +247,6 @@
|
|||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB45FAD625CA6C76005A8AC7 /* UIBarButtonItem.swift */; };
|
||||
DB47AB6227CF752B00CD73C7 /* MastodonUISnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB47AB6127CF752B00CD73C7 /* MastodonUISnapshotTests.swift */; };
|
||||
DB482A3F261331E8008AE74C /* UserTimelineViewModel+State.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB482A3E261331E8008AE74C /* UserTimelineViewModel+State.swift */; };
|
||||
DB4932B126F1FB5300EF46D4 /* WizardCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4932B026F1FB5300EF46D4 /* WizardCardView.swift */; };
|
||||
DB4932B926F31AD300EF46D4 /* BadgeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4932B826F31AD300EF46D4 /* BadgeButton.swift */; };
|
||||
DB4AA6B327BA34B6009EC082 /* CellFrameCacheContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4AA6B227BA34B6009EC082 /* CellFrameCacheContainer.swift */; };
|
||||
DB4F0963269ED06300D62E92 /* SearchResultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB4F0962269ED06300D62E92 /* SearchResultViewController.swift */; };
|
||||
|
@ -314,7 +313,6 @@
|
|||
DB6746EB278ED8B0008A6B94 /* PollOptionView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */; };
|
||||
DB6746ED278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EC278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift */; };
|
||||
DB6746F0278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6746EF278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift */; };
|
||||
DB67D08627312E67006A36CF /* WizardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB67D08527312E67006A36CF /* WizardViewController.swift */; };
|
||||
DB6804662636DC9000430867 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
||||
DB68586425E619B700F0A850 /* NSKeyValueObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */; };
|
||||
DB68A04A25E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A04925E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift */; };
|
||||
|
@ -904,7 +902,6 @@
|
|||
DB47AB6127CF752B00CD73C7 /* MastodonUISnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonUISnapshotTests.swift; sourceTree = "<group>"; };
|
||||
DB47AB6327CF858400CD73C7 /* AppStoreSnapshotTestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AppStoreSnapshotTestPlan.xctestplan; sourceTree = "<group>"; };
|
||||
DB482A3E261331E8008AE74C /* UserTimelineViewModel+State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserTimelineViewModel+State.swift"; sourceTree = "<group>"; };
|
||||
DB4932B026F1FB5300EF46D4 /* WizardCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardCardView.swift; sourceTree = "<group>"; };
|
||||
DB4932B826F31AD300EF46D4 /* BadgeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeButton.swift; sourceTree = "<group>"; };
|
||||
DB4AA6B227BA34B6009EC082 /* CellFrameCacheContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellFrameCacheContainer.swift; sourceTree = "<group>"; };
|
||||
DB4B777F26CA4EFA00B087B3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||
|
@ -1000,7 +997,6 @@
|
|||
DB6746EA278ED8B0008A6B94 /* PollOptionView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PollOptionView+Configuration.swift"; sourceTree = "<group>"; };
|
||||
DB6746EC278F45F0008A6B94 /* AutoGenerateProtocolRelayDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoGenerateProtocolRelayDelegate.swift; sourceTree = "<group>"; };
|
||||
DB6746EF278F463B008A6B94 /* AutoGenerateProtocolDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoGenerateProtocolDelegate.swift; sourceTree = "<group>"; };
|
||||
DB67D08527312E67006A36CF /* WizardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardViewController.swift; sourceTree = "<group>"; };
|
||||
DB68053E2638011000430867 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = "<group>"; };
|
||||
DB68586325E619B700F0A850 /* NSKeyValueObservation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSKeyValueObservation.swift; sourceTree = "<group>"; };
|
||||
DB68A04925E9027700CFDF14 /* AdaptiveStatusBarStyleNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdaptiveStatusBarStyleNavigationController.swift; sourceTree = "<group>"; };
|
||||
|
@ -2360,14 +2356,6 @@
|
|||
path = Template;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB67D08727312E6A006A36CF /* Wizard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB67D08527312E67006A36CF /* WizardViewController.swift */,
|
||||
);
|
||||
path = Wizard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB68A03825E900CC00CFDF14 /* Share */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2522,7 +2510,6 @@
|
|||
DB6180E426391A500018D199 /* Transition */,
|
||||
DB852D1D26FB021900FC9D81 /* Root */,
|
||||
DB01409B25C40BB600F9F3CF /* Onboarding */,
|
||||
DB67D08727312E6A006A36CF /* Wizard */,
|
||||
DB9F58ED26EF435800E7BBE9 /* Account */,
|
||||
2D38F1D325CD463600561493 /* HomeTimeline */,
|
||||
0F2021F5261325ED000C64BF /* HashtagTimeline */,
|
||||
|
@ -2775,7 +2762,6 @@
|
|||
children = (
|
||||
D8A6FE5929323DC200666A47 /* Pages */,
|
||||
DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */,
|
||||
DB4932B026F1FB5300EF46D4 /* WizardCardView.swift */,
|
||||
DB0617EA277EF3820030EE79 /* GradientBorderView.swift */,
|
||||
);
|
||||
path = View;
|
||||
|
@ -3745,7 +3731,6 @@
|
|||
5B90C45F262599800002E742 /* SettingsToggleTableViewCell.swift in Sources */,
|
||||
2D694A7425F9EB4E0038ADDC /* ContentWarningOverlayView.swift in Sources */,
|
||||
DB0FCB7827957678006C02E2 /* DataSourceProvider+UITableViewDelegate.swift in Sources */,
|
||||
DB4932B126F1FB5300EF46D4 /* WizardCardView.swift in Sources */,
|
||||
DB9A486C26032AC1008B817C /* AttachmentContainerView+EmptyStateView.swift in Sources */,
|
||||
5D0393902612D259007FE196 /* WebViewController.swift in Sources */,
|
||||
DB98EB6227B215EB0082E365 /* ReportResultViewController.swift in Sources */,
|
||||
|
@ -3903,7 +3888,6 @@
|
|||
DBB45B5627B39FC9002DC5A7 /* MediaPreviewVideoViewController.swift in Sources */,
|
||||
D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */,
|
||||
DB0FCB8027968F70006C02E2 /* MastodonStatusThreadViewModel.swift in Sources */,
|
||||
DB67D08627312E67006A36CF /* WizardViewController.swift in Sources */,
|
||||
DB6746EB278ED8B0008A6B94 /* PollOptionView+Configuration.swift in Sources */,
|
||||
DB0F9D54283EB3C000379AF8 /* ProfileHeaderView+ViewModel.swift in Sources */,
|
||||
2A3F6FE3292ECB5E002E6DA7 /* FollowedTagsViewModel.swift in Sources */,
|
||||
|
|
|
@ -28,7 +28,6 @@ final public class SceneCoordinator {
|
|||
|
||||
private(set) weak var tabBarController: MainTabBarController!
|
||||
private(set) weak var splitViewController: RootSplitViewController?
|
||||
private(set) var wizardViewController: WizardViewController?
|
||||
|
||||
private(set) var secondaryStackHashValues = Set<Int>()
|
||||
|
||||
|
@ -249,19 +248,6 @@ extension SceneCoordinator {
|
|||
transition: .modal(animated: true, completion: nil)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let wizardViewController = WizardViewController()
|
||||
if !wizardViewController.items.isEmpty,
|
||||
let delegate = rootViewController as? WizardViewControllerDelegate
|
||||
{
|
||||
// do not add as child view controller.
|
||||
// otherwise, the tab bar controller will add as a new tab
|
||||
wizardViewController.delegate = delegate
|
||||
wizardViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
wizardViewController.view.frame = rootViewController.view.bounds
|
||||
rootViewController.view.addSubview(wizardViewController.view)
|
||||
self.wizardViewController = wizardViewController
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
//
|
||||
// WizardCardView.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-9-15.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
final class WizardCardView: UIView {
|
||||
|
||||
static let bubbleArrowHeight: CGFloat = 17
|
||||
static let bubbleArrowWidth: CGFloat = 20
|
||||
|
||||
let contentView = UIView()
|
||||
|
||||
let backgroundShapeLayer = CAShapeLayer()
|
||||
var arrowRectCorner: UIRectCorner = .bottomRight
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
|
||||
label.textColor = .black
|
||||
return label
|
||||
}()
|
||||
|
||||
let descriptionLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .systemFont(ofSize: 13, weight: .regular))
|
||||
label.textColor = .black
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension WizardCardView {
|
||||
private func _init() {
|
||||
layer.masksToBounds = false
|
||||
layer.addSublayer(backgroundShapeLayer)
|
||||
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(contentView)
|
||||
NSLayoutConstraint.activate([
|
||||
contentView.topAnchor.constraint(equalTo: topAnchor, constant: WizardCardView.bubbleArrowHeight),
|
||||
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: WizardCardView.bubbleArrowHeight),
|
||||
])
|
||||
|
||||
let containerStackView = UIStackView()
|
||||
containerStackView.axis = .vertical
|
||||
containerStackView.spacing = 2
|
||||
containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(containerStackView)
|
||||
NSLayoutConstraint.activate([
|
||||
containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5),
|
||||
containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 7),
|
||||
contentView.trailingAnchor.constraint(equalTo: containerStackView.trailingAnchor, constant: 24),
|
||||
contentView.bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 5),
|
||||
])
|
||||
|
||||
containerStackView.addArrangedSubview(titleLabel)
|
||||
containerStackView.addArrangedSubview(descriptionLabel)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
let radius: CGFloat = 5
|
||||
let rect = contentView.frame
|
||||
let path = UIBezierPath()
|
||||
|
||||
switch arrowRectCorner {
|
||||
case .bottomRight:
|
||||
path.move(to: CGPoint(x: rect.maxX - WizardCardView.bubbleArrowWidth, y: rect.maxY + radius))
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: .pi, endAngle: .pi / 2 * 3, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: .pi / 2 * 3, endAngle: .pi * 2, clockwise: true)
|
||||
path.addLine(to: CGPoint(x: rect.maxX + radius, y: rect.maxY + radius + WizardCardView.bubbleArrowHeight))
|
||||
path.close()
|
||||
case .bottomLeft:
|
||||
path.move(to: CGPoint(x: rect.minX + WizardCardView.bubbleArrowWidth, y: rect.maxY + radius))
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius, startAngle: .pi / 2, endAngle: 0, clockwise: false)
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: 0, endAngle: -.pi / 2, clockwise: false)
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: -.pi / 2, endAngle: -.pi, clockwise: false)
|
||||
path.addLine(to: CGPoint(x: rect.minX - radius, y: rect.maxY + radius + WizardCardView.bubbleArrowHeight))
|
||||
path.close()
|
||||
case .allCorners: // no arrow
|
||||
path.move(to: CGPoint(x: rect.maxX, y: rect.maxY + radius))
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.maxY), radius: radius, startAngle: .pi / 2, endAngle: .pi, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.minX, y: rect.minY), radius: radius, startAngle: .pi, endAngle: .pi / 2 * 3, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.minY), radius: radius, startAngle: .pi / 2 * 3, endAngle: .pi * 2, clockwise: true)
|
||||
path.addArc(withCenter: CGPoint(x: rect.maxX, y: rect.maxY), radius: radius, startAngle: .pi * 2, endAngle: .pi / 2 * 5, clockwise: true)
|
||||
path.close()
|
||||
default:
|
||||
assertionFailure("FIXME")
|
||||
}
|
||||
|
||||
backgroundShapeLayer.lineCap = .round
|
||||
backgroundShapeLayer.lineJoin = .round
|
||||
backgroundShapeLayer.lineWidth = 3
|
||||
backgroundShapeLayer.strokeColor = UIColor.white.cgColor
|
||||
backgroundShapeLayer.fillColor = UIColor.white.cgColor
|
||||
backgroundShapeLayer.path = path.cgPath
|
||||
}
|
||||
|
||||
override var isAccessibilityElement: Bool {
|
||||
get { true }
|
||||
set { }
|
||||
}
|
||||
|
||||
override var accessibilityLabel: String? {
|
||||
get {
|
||||
return [
|
||||
titleLabel.text,
|
||||
descriptionLabel.text
|
||||
]
|
||||
.compactMap { $0 }
|
||||
.joined(separator: " ")
|
||||
}
|
||||
set { }
|
||||
}
|
||||
|
||||
override var accessibilityHint: String? {
|
||||
get {
|
||||
return L10n.Scene.Wizard.accessibilityHint
|
||||
}
|
||||
set { }
|
||||
}
|
||||
}
|
|
@ -544,60 +544,6 @@ extension MainTabBarController: UITabBarControllerDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - WizardViewControllerDelegate
|
||||
extension MainTabBarController: WizardViewControllerDelegate {
|
||||
func readyToLayoutItem(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> Bool {
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
return isReadyForWizardAvatarButton
|
||||
}
|
||||
}
|
||||
|
||||
func layoutSpotlight(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> UIBezierPath {
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
guard let avatarButtonFrameInView = avatarButtonFrameInWizardView(wizardView: wizardViewController.view) else {
|
||||
return UIBezierPath()
|
||||
}
|
||||
return UIBezierPath(ovalIn: avatarButtonFrameInView)
|
||||
}
|
||||
}
|
||||
|
||||
func layoutWizardCard(_ wizardViewController: WizardViewController, item: WizardViewController.Item) {
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
guard let avatarButtonFrameInView = avatarButtonFrameInWizardView(wizardView: wizardViewController.view) else {
|
||||
return
|
||||
}
|
||||
let anchorView = UIView()
|
||||
anchorView.frame = avatarButtonFrameInView
|
||||
wizardViewController.backgroundView.addSubview(anchorView)
|
||||
|
||||
let wizardCardView = WizardCardView()
|
||||
wizardCardView.arrowRectCorner = view.traitCollection.layoutDirection == .leftToRight ? .bottomRight : .bottomLeft
|
||||
wizardCardView.titleLabel.text = item.title
|
||||
wizardCardView.descriptionLabel.text = item.description
|
||||
|
||||
wizardCardView.translatesAutoresizingMaskIntoConstraints = false
|
||||
wizardViewController.backgroundView.addSubview(wizardCardView)
|
||||
NSLayoutConstraint.activate([
|
||||
anchorView.topAnchor.constraint(equalTo: wizardCardView.bottomAnchor, constant: 13), // 13pt spacing
|
||||
wizardCardView.trailingAnchor.constraint(equalTo: anchorView.centerXAnchor),
|
||||
wizardCardView.widthAnchor.constraint(equalTo: wizardViewController.view.widthAnchor, multiplier: 2.0/3.0).priority(.required - 1),
|
||||
])
|
||||
wizardCardView.setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||
}
|
||||
}
|
||||
|
||||
private func avatarButtonFrameInWizardView(wizardView: UIView) -> CGRect? {
|
||||
guard let superview = avatarButton.superview else {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
return superview.convert(avatarButton.frame, to: wizardView)
|
||||
}
|
||||
}
|
||||
|
||||
// HIG: keyboard UX
|
||||
// https://developer.apple.com/design/human-interface-guidelines/macos/user-interaction/keyboard/
|
||||
extension MainTabBarController {
|
||||
|
|
|
@ -255,90 +255,3 @@ extension RootSplitViewController: UISplitViewControllerDelegate {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - WizardViewControllerDelegate
|
||||
extension RootSplitViewController: WizardViewControllerDelegate {
|
||||
|
||||
func readyToLayoutItem(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> Bool {
|
||||
guard traitCollection.horizontalSizeClass != .compact else {
|
||||
return compactMainTabBarViewController.readyToLayoutItem(wizardViewController, item: item)
|
||||
}
|
||||
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
return contentSplitViewController.sidebarViewController.viewModel.isReadyForWizardAvatarButton
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func layoutSpotlight(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> UIBezierPath {
|
||||
guard traitCollection.horizontalSizeClass != .compact else {
|
||||
return compactMainTabBarViewController.layoutSpotlight(wizardViewController, item: item)
|
||||
}
|
||||
|
||||
switch item {
|
||||
case .multipleAccountSwitch:
|
||||
guard let frame = avatarButtonFrameInWizardView(wizardView: wizardViewController.view)
|
||||
else {
|
||||
assertionFailure()
|
||||
return UIBezierPath()
|
||||
}
|
||||
return UIBezierPath(ovalIn: frame)
|
||||
}
|
||||
}
|
||||
|
||||
func layoutWizardCard(_ wizardViewController: WizardViewController, item: WizardViewController.Item) {
|
||||
guard traitCollection.horizontalSizeClass != .compact else {
|
||||
return compactMainTabBarViewController.layoutWizardCard(wizardViewController, item: item)
|
||||
}
|
||||
|
||||
guard let frame = avatarButtonFrameInWizardView(wizardView: wizardViewController.view) else {
|
||||
return
|
||||
}
|
||||
|
||||
let anchorView = UIView()
|
||||
anchorView.frame = frame
|
||||
wizardViewController.backgroundView.addSubview(anchorView)
|
||||
|
||||
let wizardCardView = WizardCardView()
|
||||
wizardCardView.arrowRectCorner = .allCorners // no arrow
|
||||
wizardCardView.titleLabel.text = item.title
|
||||
wizardCardView.descriptionLabel.text = item.description
|
||||
|
||||
wizardCardView.translatesAutoresizingMaskIntoConstraints = false
|
||||
wizardViewController.backgroundView.addSubview(wizardCardView)
|
||||
NSLayoutConstraint.activate([
|
||||
wizardCardView.centerYAnchor.constraint(equalTo: anchorView.centerYAnchor),
|
||||
wizardCardView.leadingAnchor.constraint(equalTo: anchorView.trailingAnchor, constant: 20), // 20pt spacing
|
||||
wizardCardView.widthAnchor.constraint(equalToConstant: 320),
|
||||
])
|
||||
wizardCardView.setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||
}
|
||||
|
||||
private func avatarButtonFrameInWizardView(wizardView: UIView) -> CGRect? {
|
||||
guard let diffableDataSource = contentSplitViewController.sidebarViewController.viewModel.diffableDataSource,
|
||||
let indexPath = diffableDataSource.indexPath(for: .tab(.me)),
|
||||
let cell = contentSplitViewController.sidebarViewController.collectionView.cellForItem(at: indexPath) as? SidebarListCollectionViewCell,
|
||||
let contentView = cell._contentView,
|
||||
let frame = sourceViewFrameInTargetView(
|
||||
sourceView: contentView.avatarButton,
|
||||
targetView: wizardView
|
||||
)
|
||||
else {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
return frame
|
||||
}
|
||||
|
||||
private func sourceViewFrameInTargetView(
|
||||
sourceView: UIView,
|
||||
targetView: UIView
|
||||
) -> CGRect? {
|
||||
guard let superview = sourceView.superview else {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
return superview.convert(sourceView.frame, to: targetView)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,213 +0,0 @@
|
|||
//
|
||||
// WizardViewController.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by Cirno MainasuK on 2021-11-2.
|
||||
//
|
||||
|
||||
import os.log
|
||||
import UIKit
|
||||
import Combine
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
|
||||
protocol WizardViewControllerDelegate: AnyObject {
|
||||
func readyToLayoutItem(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> Bool
|
||||
func layoutSpotlight(_ wizardViewController: WizardViewController, item: WizardViewController.Item) -> UIBezierPath
|
||||
func layoutWizardCard(_ wizardViewController: WizardViewController, item: WizardViewController.Item)
|
||||
}
|
||||
|
||||
class WizardViewController: UIViewController {
|
||||
|
||||
let logger = Logger(subsystem: "Wizard", category: "UI")
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
weak var delegate: WizardViewControllerDelegate?
|
||||
|
||||
private(set) var items: [Item] = {
|
||||
var items: [Item] = []
|
||||
if !UserDefaults.shared.didShowMultipleAccountSwitchWizard {
|
||||
items.append(.multipleAccountSwitch)
|
||||
}
|
||||
return items
|
||||
}()
|
||||
|
||||
let pendingItem = CurrentValueSubject<Item?, Never>(nil)
|
||||
let currentItem = CurrentValueSubject<Item?, Never>(nil)
|
||||
|
||||
let backgroundView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
return view
|
||||
}()
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension WizardViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setup()
|
||||
|
||||
let backgroundTapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
|
||||
backgroundTapGestureRecognizer.addTarget(self, action: #selector(WizardViewController.backgroundTapGestureRecognizerHandler(_:)))
|
||||
backgroundView.addGestureRecognizer(backgroundTapGestureRecognizer)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
// Create a timer to consume pending item
|
||||
Timer.publish(every: 0.5, on: .main, in: .default)
|
||||
.autoconnect()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
guard self.pendingItem.value != nil else { return }
|
||||
self.consume()
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
consume()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
invalidLayoutForCurrentItem()
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
coordinator.animate { context in
|
||||
|
||||
} completion: { [weak self] context in
|
||||
guard let self = self else { return }
|
||||
self.invalidLayoutForCurrentItem()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension WizardViewController {
|
||||
enum Item {
|
||||
case multipleAccountSwitch
|
||||
|
||||
var title: String {
|
||||
return L10n.Scene.Wizard.newInMastodon
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .multipleAccountSwitch:
|
||||
return L10n.Scene.Wizard.multipleAccountSwitchIntroDescription
|
||||
}
|
||||
}
|
||||
|
||||
func markAsRead() {
|
||||
switch self {
|
||||
case .multipleAccountSwitch:
|
||||
UserDefaults.shared.didShowMultipleAccountSwitchWizard = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WizardViewController {
|
||||
|
||||
func setup() {
|
||||
assert(delegate != nil, "need set delegate before use")
|
||||
|
||||
guard !items.isEmpty else { return }
|
||||
|
||||
backgroundView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
backgroundView.frame = view.bounds
|
||||
view.addSubview(backgroundView)
|
||||
}
|
||||
|
||||
func destroy() {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
|
||||
func consume() {
|
||||
guard !items.isEmpty else {
|
||||
destroy()
|
||||
return
|
||||
}
|
||||
|
||||
guard let first = items.first else { return }
|
||||
guard delegate?.readyToLayoutItem(self, item: first) == true else {
|
||||
pendingItem.value = first
|
||||
return
|
||||
}
|
||||
pendingItem.value = nil
|
||||
currentItem.value = nil
|
||||
|
||||
let item = items.removeFirst()
|
||||
perform(item: item)
|
||||
}
|
||||
|
||||
private func perform(item: Item) {
|
||||
guard let delegate = delegate else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
// prepare for reuse
|
||||
prepareForReuse()
|
||||
|
||||
// set wizard item read
|
||||
item.markAsRead()
|
||||
|
||||
// add spotlight
|
||||
let spotlight = delegate.layoutSpotlight(self, item: item)
|
||||
let maskLayer = CAShapeLayer()
|
||||
// expand rect to make sure view always fill the screen when device rotate
|
||||
let expandRect: CGRect = {
|
||||
var rect = backgroundView.bounds
|
||||
rect.size.width *= 2
|
||||
rect.size.height *= 2
|
||||
return rect
|
||||
}()
|
||||
let path = UIBezierPath(rect: expandRect)
|
||||
path.append(spotlight)
|
||||
maskLayer.fillRule = .evenOdd
|
||||
maskLayer.path = path.cgPath
|
||||
backgroundView.layer.mask = maskLayer
|
||||
|
||||
// layout wizard card
|
||||
delegate.layoutWizardCard(self, item: item)
|
||||
|
||||
currentItem.value = item
|
||||
}
|
||||
|
||||
private func prepareForReuse() {
|
||||
backgroundView.subviews.forEach { subview in
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
backgroundView.mask = nil
|
||||
backgroundView.layer.mask = nil
|
||||
}
|
||||
|
||||
private func invalidLayoutForCurrentItem() {
|
||||
if let item = currentItem.value {
|
||||
perform(item: item)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension WizardViewController {
|
||||
@objc private func backgroundTapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
|
||||
consume()
|
||||
}
|
||||
}
|
|
@ -1530,14 +1530,6 @@ public enum L10n {
|
|||
}
|
||||
}
|
||||
}
|
||||
public enum Wizard {
|
||||
/// Double tap to dismiss this wizard
|
||||
public static let accessibilityHint = L10n.tr("Localizable", "Scene.Wizard.AccessibilityHint", fallback: "Double tap to dismiss this wizard")
|
||||
/// Switch between multiple accounts by holding the profile button.
|
||||
public static let multipleAccountSwitchIntroDescription = L10n.tr("Localizable", "Scene.Wizard.MultipleAccountSwitchIntroDescription", fallback: "Switch between multiple accounts by holding the profile button.")
|
||||
/// New in Mastodon
|
||||
public static let newInMastodon = L10n.tr("Localizable", "Scene.Wizard.NewInMastodon", fallback: "New in Mastodon")
|
||||
}
|
||||
}
|
||||
public enum Widget {
|
||||
public enum Common {
|
||||
|
|
|
@ -535,9 +535,6 @@ You can still send and receive emails from anyone, even if their email ends in @
|
|||
"Scene.Welcome.LogIn" = "Log In";
|
||||
"Scene.Welcome.Slogan" = "Social networking
|
||||
back in your hands.";
|
||||
"Scene.Wizard.AccessibilityHint" = "Double tap to dismiss this wizard";
|
||||
"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Switch between multiple accounts by holding the profile button.";
|
||||
"Scene.Wizard.NewInMastodon" = "New in Mastodon";
|
||||
"Widget.Common.UnsupportedWidgetFamily" = "Sorry but this Widget family is unsupported.";
|
||||
"Widget.Common.UserNotLoggedIn" = "Please open Mastodon to log in to an Account.";
|
||||
"Widget.FollowersCount.ConfigurationDescription" = "Show number of followers.";
|
||||
|
|
|
@ -535,9 +535,6 @@ You can still send and receive emails from anyone, even if their email ends in @
|
|||
"Scene.Welcome.LogIn" = "Log In";
|
||||
"Scene.Welcome.Slogan" = "Social networking
|
||||
back in your hands.";
|
||||
"Scene.Wizard.AccessibilityHint" = "Double tap to dismiss this wizard";
|
||||
"Scene.Wizard.MultipleAccountSwitchIntroDescription" = "Switch between multiple accounts by holding the profile button.";
|
||||
"Scene.Wizard.NewInMastodon" = "New in Mastodon";
|
||||
"Widget.Common.UnsupportedWidgetFamily" = "Sorry but this Widget family is unsupported.";
|
||||
"Widget.Common.UserNotLoggedIn" = "Please open Mastodon to log in to an Account.";
|
||||
"Widget.FollowersCount.ConfigurationDescription" = "Show number of followers.";
|
||||
|
|
Loading…
Reference in New Issue