diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/Contents.json b/Projects/Presentation/Resources/Images.xcassets/Tutorial/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Projects/Presentation/Resources/Images.xcassets/Tutorial/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/Contents.json b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/Contents.json new file mode 100644 index 0000000..63a1e9f --- /dev/null +++ b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "tutorial_edit_routine_graphic.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "tutorial_edit_routine_graphic@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "tutorial_edit_routine_graphic@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic.png new file mode 100644 index 0000000..4932730 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic@2x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic@2x.png new file mode 100644 index 0000000..7a9d517 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic@2x.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic@3x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic@3x.png new file mode 100644 index 0000000..086e3d8 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_edit_routine_graphic.imageset/tutorial_edit_routine_graphic@3x.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/Contents.json b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/Contents.json new file mode 100644 index 0000000..a42db16 --- /dev/null +++ b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "tutorial_emotion_graphic.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "tutorial_emotion_graphic@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "tutorial_emotion_graphic@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic.png new file mode 100644 index 0000000..f1c2188 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic@2x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic@2x.png new file mode 100644 index 0000000..b85522b Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic@2x.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic@3x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic@3x.png new file mode 100644 index 0000000..51cc9a7 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_emotion_graphic.imageset/tutorial_emotion_graphic@3x.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/Contents.json b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/Contents.json new file mode 100644 index 0000000..98852b5 --- /dev/null +++ b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "tutorial_home_more_graphic.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "tutorial_home_more_graphic@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "tutorial_home_more_graphic@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic.png new file mode 100644 index 0000000..18fe6fe Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic@2x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic@2x.png new file mode 100644 index 0000000..3c36ce0 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic@2x.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic@3x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic@3x.png new file mode 100644 index 0000000..a41e553 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_home_more_graphic.imageset/tutorial_home_more_graphic@3x.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/Contents.json b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/Contents.json new file mode 100644 index 0000000..09368e8 --- /dev/null +++ b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "tutorial_recommendation_routine_graphic.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "tutorial_recommendation_routine_graphic@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "tutorial_recommendation_routine_graphic@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic.png new file mode 100644 index 0000000..87ef146 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic@2x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic@2x.png new file mode 100644 index 0000000..d627d12 Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic@2x.png differ diff --git a/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic@3x.png b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic@3x.png new file mode 100644 index 0000000..b7cc6fb Binary files /dev/null and b/Projects/Presentation/Resources/Images.xcassets/Tutorial/tutorial_recommendation_routine_graphic.imageset/tutorial_recommendation_routine_graphic@3x.png differ diff --git a/Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift b/Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift index 2cb875d..3088d68 100644 --- a/Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift +++ b/Projects/Presentation/Sources/Common/DesignSystem/BitnagilGraphic.swift @@ -33,4 +33,10 @@ enum BitnagilGraphic { static let logoGraphic = UIImage(named: "bitnagil_logo", in: bundle, with: nil) static let profileGraphic = UIImage(named: "profile_graphic", in: bundle, with: nil) static let grayLogoGraphic = UIImage(named: "bitnagil_logo_gray", in: bundle, with: nil) + + // MARK: - Tutorial + static let tutorialEmotionGraphic = UIImage(named: "tutorial_emotion_graphic", in: bundle, with: nil) + static let tutorialHomeMoreGraphic = UIImage(named: "tutorial_home_more_graphic", in: bundle, with: nil) + static let tutorialRecommendationRoutineGraphic = UIImage(named: "tutorial_recommendation_routine_graphic", in: bundle, with: nil) + static let tutorialEditRoutineGraphic = UIImage(named: "tutorial_edit_routine_graphic", in: bundle, with: nil) } diff --git a/Projects/Presentation/Sources/Home/View/HomeViewController.swift b/Projects/Presentation/Sources/Home/View/HomeViewController.swift index e72e851..c40f1f1 100644 --- a/Projects/Presentation/Sources/Home/View/HomeViewController.swift +++ b/Projects/Presentation/Sources/Home/View/HomeViewController.swift @@ -118,6 +118,14 @@ final class HomeViewController: BaseViewController { helpButton.setImage(BitnagilIcon.helpIcon, for: .normal) alarmButton.setImage(BitnagilIcon.alarmIcon, for: .normal) + helpButton.addAction( + UIAction { [weak self] _ in + let tutorialView = TutorialViewController() + tutorialView.hidesBottomBarWhenPushed = true + self?.navigationController?.pushViewController(tutorialView, animated: true) + }, + for: .touchUpInside) + let homeLabelText = "\(nickname)\n" homeLabel.attributedText = BitnagilFont( family: .cafe24Ssurround, diff --git a/Projects/Presentation/Sources/Tutorial/Model/Tutorial.swift b/Projects/Presentation/Sources/Tutorial/Model/Tutorial.swift new file mode 100644 index 0000000..2f05511 --- /dev/null +++ b/Projects/Presentation/Sources/Tutorial/Model/Tutorial.swift @@ -0,0 +1,54 @@ +// +// Tutorial.swift +// Presentation +// +// Created by 최정인 on 9/3/25. +// + +import UIKit + +enum Tutorial: CaseIterable { + case emotionRegister + case homeMore + case recommendationRoutine + case editRoutine + + var title: String { + switch self { + case .emotionRegister: + "오늘 감정 등록하기란?" + case .homeMore: + "홈에서 더보기란?" + case .recommendationRoutine: + "맞춤 추천 루틴이란?" + case .editRoutine: + "루틴 수정하기란?" + } + } + + var description: String { + switch self { + case .emotionRegister: + "오늘 느끼는 감정을 선택하면, 그 기분에 맞춰 추천 루틴을\n확인할 수 있어요." + case .homeMore: + "루틴 리스트에서 세부 항목(세부 루틴, 반복, 기간, 시간)을\n확인하고, 필요 시 수정하거나 삭제할 수 있어요." + case .recommendationRoutine: + "처음 설정한 목표에 맞춰 루틴을 추천받을 수 있으며,\n필요할 때 언제든 수정할 수 있어요." + case .editRoutine: + "등록한 루틴은 수정할 수 있으며, 변경 내용을 당일부터 또는\n다음 날부터 적용하도록 선택할 수 있어요." + } + } + + var tutorialImage: UIImage? { + switch self { + case .emotionRegister: + BitnagilGraphic.tutorialEmotionGraphic + case .homeMore: + BitnagilGraphic.tutorialHomeMoreGraphic + case .recommendationRoutine: + BitnagilGraphic.tutorialRecommendationRoutineGraphic + case .editRoutine: + BitnagilGraphic.tutorialEditRoutineGraphic + } + } +} diff --git a/Projects/Presentation/Sources/Tutorial/View/Component/TutorialTableCell.swift b/Projects/Presentation/Sources/Tutorial/View/Component/TutorialTableCell.swift new file mode 100644 index 0000000..c35fb52 --- /dev/null +++ b/Projects/Presentation/Sources/Tutorial/View/Component/TutorialTableCell.swift @@ -0,0 +1,62 @@ +// +// TutorialTableCell.swift +// Presentation +// +// Created by 최정인 on 9/3/25. +// + +import SnapKit +import UIKit + +final class TutorialTableCell: UITableViewCell { + private enum Layout { + static let titleLableLeadingSpacing: CGFloat = 20 + static let chevronIconSize: CGFloat = 24 + static let chevronIconTrailingSpacing: CGFloat = 11 + } + + private let titleLabel = UILabel() + private let chevronIcon = UIImageView() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + configureAttribute() + configureLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(title: String) { + titleLabel.text = title + } + + private func configureAttribute() { + layer.masksToBounds = true + layer.cornerRadius = 12 + backgroundColor = BitnagilColor.gray99 + + titleLabel.font = BitnagilFont(style: .body2, weight: .medium).font + titleLabel.textColor = BitnagilColor.gray10 + + chevronIcon.image = BitnagilIcon.chevronRightIcon + chevronIcon.contentMode = .scaleAspectFit + } + + private func configureLayout() { + contentView.addSubview(titleLabel) + contentView.addSubview(chevronIcon) + + titleLabel.snp.makeConstraints { make in + make.leading.equalToSuperview().offset(Layout.titleLableLeadingSpacing) + make.centerY.equalToSuperview() + } + + chevronIcon.snp.makeConstraints { make in + make.size.equalTo(Layout.chevronIconSize) + make.centerY.equalToSuperview() + make.trailing.equalToSuperview().inset(Layout.chevronIconTrailingSpacing) + } + } +} diff --git a/Projects/Presentation/Sources/Tutorial/View/TutorialDetailViewController.swift b/Projects/Presentation/Sources/Tutorial/View/TutorialDetailViewController.swift new file mode 100644 index 0000000..502ab57 --- /dev/null +++ b/Projects/Presentation/Sources/Tutorial/View/TutorialDetailViewController.swift @@ -0,0 +1,91 @@ +// +// TutorialDetailViewController.swift +// Presentation +// +// Created by 최정인 on 9/3/25. +// + +import SnapKit +import UIKit + +final class TutorialDetailViewController: UIViewController { + private enum Layout { + static let horizontalMargin: CGFloat = 24 + static let titleLabelTopSpacing: CGFloat = 26 + static let titleLabelHeight: CGFloat = 24 + static let descriptionLabelTopSpacing: CGFloat = 10 + static let descriptionLabelHeight: CGFloat = 40 + static let tutorialImageTopSpacing: CGFloat = 24 + } + + private let titleLabel = UILabel() + private let descriptionLabel = UILabel() + private let tutorialImage = UIImageView() + var onDismiss: (() -> Void)? + private let tutorial: Tutorial + + init(tutorial: Tutorial) { + self.tutorial = tutorial + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + configureAttribute() + configureLayout() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + onDismiss?() + } + + private func configureAttribute() { + view.backgroundColor = BitnagilColor.gray99 + + titleLabel.text = tutorial.title + titleLabel.font = BitnagilFont(style: .title3, weight: .semiBold).font + titleLabel.textColor = BitnagilColor.gray10 + + let descriptionText = tutorial.description + descriptionLabel.attributedText = BitnagilFont(style: .body2, weight: .medium).attributedString(text: descriptionText) + descriptionLabel.textColor = BitnagilColor.gray40 + descriptionLabel.numberOfLines = 2 + + tutorialImage.image = tutorial.tutorialImage + tutorialImage.contentMode = .scaleAspectFit + } + + private func configureLayout() { + let safeArea = view.safeAreaLayoutGuide + + view.addSubview(titleLabel) + view.addSubview(descriptionLabel) + view.addSubview(tutorialImage) + + titleLabel.snp.makeConstraints { make in + make.top.equalTo(safeArea).offset(Layout.titleLabelTopSpacing) + make.leading.equalTo(safeArea).offset(Layout.horizontalMargin) + make.height.equalTo(Layout.titleLabelHeight) + } + + descriptionLabel.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(Layout.descriptionLabelTopSpacing) + make.horizontalEdges.equalTo(safeArea).inset(Layout.horizontalMargin) + make.height.equalTo(Layout.descriptionLabelHeight) + } + + tutorialImage.snp.makeConstraints { make in + make.top.equalTo(descriptionLabel.snp.bottom).offset(Layout.tutorialImageTopSpacing) + make.horizontalEdges.equalTo(safeArea).inset(Layout.horizontalMargin) + if let image = tutorialImage.image { + let aspectRatio = image.size.height / image.size.width + make.height.equalTo(tutorialImage.snp.width).multipliedBy(aspectRatio) + } + } + } +} diff --git a/Projects/Presentation/Sources/Tutorial/View/TutorialViewController.swift b/Projects/Presentation/Sources/Tutorial/View/TutorialViewController.swift new file mode 100644 index 0000000..17350e5 --- /dev/null +++ b/Projects/Presentation/Sources/Tutorial/View/TutorialViewController.swift @@ -0,0 +1,126 @@ +// +// TutorialViewController.swift +// Presentation +// +// Created by 최정인 on 9/3/25. +// + +import SnapKit +import UIKit + +final class TutorialViewController: UIViewController { + private enum Layout { + static let horizontalMargin: CGFloat = 20 + static let tutorialTableViewTopSpacing: CGFloat = 86 + static let tutorialTableViewCellHeight: CGFloat = 56 + static let tutorialTableViewCellSpacing: CGFloat = 12 + static let tutorialDetailViewHeight: CGFloat = 348 + } + + private var dimmedView: UIView? + private let tutorialTableView = UITableView() + + override func viewDidLoad() { + super.viewDidLoad() + configureAttribute() + configureLayout() + } + + private func configureAttribute() { + view.backgroundColor = .systemBackground + navigationController?.setNavigationBarHidden(true, animated: false) + configureCustomNavigationBar(navigationBarStyle: .withBackButton(title: "설명서")) + + tutorialTableView.delegate = self + tutorialTableView.dataSource = self + + tutorialTableView.register(TutorialTableCell.self, forCellReuseIdentifier: TutorialTableCell.className) + tutorialTableView.separatorStyle = .none + tutorialTableView.isScrollEnabled = false + } + + private func configureLayout() { + let safeArea = view.safeAreaLayoutGuide + + view.addSubview(tutorialTableView) + tutorialTableView.snp.makeConstraints { make in + make.top.equalTo(safeArea).offset(Layout.tutorialTableViewTopSpacing) + make.horizontalEdges.equalTo(safeArea).inset(Layout.horizontalMargin) + make.bottom.equalTo(safeArea) + } + } +} + +// MARK: UITableViewDelegate, UITableViewDataSource +extension TutorialViewController: UITableViewDelegate, UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return Tutorial.allCases.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: TutorialTableCell.className, for: indexPath) as? TutorialTableCell + else { return UITableViewCell() } + + let tutorial = Tutorial.allCases[indexPath.section] + cell.configure(title: tutorial.title) + cell.selectionStyle = .none + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return Layout.tutorialTableViewCellHeight + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return Layout.tutorialTableViewCellSpacing + } + + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + return UIView() + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let tutorial = Tutorial.allCases[indexPath.section] + + dimmedView?.removeFromSuperview() + + let newDimmedView = UIView() + newDimmedView.backgroundColor = .black.withAlphaComponent(0.0) + newDimmedView.frame = view.bounds + view.addSubview(newDimmedView) + dimmedView = newDimmedView + + UIView.animate(withDuration: 0.25) { + newDimmedView.backgroundColor = UIColor.black.withAlphaComponent(0.7) + } + + let tutorialDetailView = TutorialDetailViewController(tutorial: tutorial) + if let sheet = tutorialDetailView.sheetPresentationController { + sheet.prefersGrabberVisible = false + if #available(iOS 16.0, *) { + sheet.detents = [.custom { _ in Layout.tutorialDetailViewHeight }] + } else { + sheet.detents = [.medium()] + } + sheet.prefersScrollingExpandsWhenScrolledToEdge = false + sheet.preferredCornerRadius = 20 + } + + tutorialDetailView.onDismiss = { [weak self] in + guard let self else { return } + guard let dimmedView = self.dimmedView else { return } + UIView.animate(withDuration: 0.1, animations: { + dimmedView.alpha = 0 + }, completion: { _ in + dimmedView.removeFromSuperview() + self.dimmedView = nil + }) + } + + present(tutorialDetailView, animated: true) + } +}