Replace the pagecontrol with a collection view (#690)

Scrolling wasn't smooth with pageviews, as they do some black magic with scrollviews (like resetting contentOffset). If you depend on contentOffset, this breaks things and makes them hard.
This commit is contained in:
Nathan Mattes 2023-01-07 16:02:46 +01:00
parent 8ff47a72d0
commit 44d85e0263
4 changed files with 68 additions and 132 deletions

View File

@ -117,8 +117,7 @@
D8916DC029211BE500124085 /* ContentSizedTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8916DBF29211BE500124085 /* ContentSizedTableView.swift */; };
D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */; };
D8A6FE5B293244B500666A47 /* WelcomeContentPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6FE5A293244B500666A47 /* WelcomeContentPage.swift */; };
D8A6FE5D293244C300666A47 /* WelcomeContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6FE5C293244C300666A47 /* WelcomeContentViewController.swift */; };
D8A6FE5F29324BBC00666A47 /* WelcomeContentPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6FE5E29324BBC00666A47 /* WelcomeContentPageView.swift */; };
D8A6FE5F29324BBC00666A47 /* WelcomeContentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6FE5E29324BBC00666A47 /* WelcomeContentCollectionViewCell.swift */; };
DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; settings = {ATTRIBUTES = (codegen, ); }; };
DB0009A726AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; };
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140CE25C42AEE00F9F3CF /* OSLog.swift */; };
@ -666,8 +665,7 @@
D8916DBF29211BE500124085 /* ContentSizedTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSizedTableView.swift; sourceTree = "<group>"; };
D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginViewController.swift; sourceTree = "<group>"; };
D8A6FE5A293244B500666A47 /* WelcomeContentPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentPage.swift; sourceTree = "<group>"; };
D8A6FE5C293244C300666A47 /* WelcomeContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentViewController.swift; sourceTree = "<group>"; };
D8A6FE5E29324BBC00666A47 /* WelcomeContentPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentPageView.swift; sourceTree = "<group>"; };
D8A6FE5E29324BBC00666A47 /* WelcomeContentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentCollectionViewCell.swift; sourceTree = "<group>"; };
D8A6FE6129325F5900666A47 /* Intents.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Intents.stringsdict; sourceTree = "<group>"; };
D8A6FE6229325F5900666A47 /* app.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = app.json; sourceTree = "<group>"; };
D8A6FE6329325F5900666A47 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@ -1607,8 +1605,7 @@
isa = PBXGroup;
children = (
D8A6FE5A293244B500666A47 /* WelcomeContentPage.swift */,
D8A6FE5C293244C300666A47 /* WelcomeContentViewController.swift */,
D8A6FE5E29324BBC00666A47 /* WelcomeContentPageView.swift */,
D8A6FE5E29324BBC00666A47 /* WelcomeContentCollectionViewCell.swift */,
);
path = Pages;
sourceTree = "<group>";
@ -3336,8 +3333,7 @@
DB603113279EBEBA00A935FE /* DataSourceFacade+Block.swift in Sources */,
DB63F777279A9A2A00455B82 /* NotificationView+Configuration.swift in Sources */,
DB029E95266A20430062874E /* MastodonAuthenticationController.swift in Sources */,
DB0C947726A7FE840088FB11 /* NotificationAvatarButton.swift in Sources */,
D8A6FE5F29324BBC00666A47 /* WelcomeContentPageView.swift in Sources */,
D8A6FE5F29324BBC00666A47 /* WelcomeContentCollectionViewCell.swift in Sources */,
5B90C461262599800002E742 /* SettingsLinkTableViewCell.swift in Sources */,
DB6180DD263918E30018D199 /* MediaPreviewViewController.swift in Sources */,
DBE3CDEC261C6B2900430CC6 /* FavoriteViewController.swift in Sources */,
@ -3549,7 +3545,6 @@
DB1D84382657B275000346B3 /* SegmentedControlNavigateable.swift in Sources */,
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */,
DB7A9F932818F33C0016AF98 /* MastodonServerRulesViewController+Debug.swift in Sources */,
D8A6FE5D293244C300666A47 /* WelcomeContentViewController.swift in Sources */,
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */,
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,

View File

@ -7,25 +7,24 @@
import UIKit
class WelcomeContentPageView: UIView {
class WelcomeContentCollectionViewCell: UICollectionViewCell {
static let identifier = "WelcomeContentCollectionViewCell"
//TODO: Put in ScrollView?
private let contentStackView: UIStackView
private let titleView: UILabel
private let label: UILabel
private let blurryBackgroundView: UIVisualEffectView
init(page: WelcomeContentPage) {
override init(frame: CGRect) {
titleView = UILabel()
titleView.font = WelcomeViewController.largeTitleFont
titleView.textColor = WelcomeViewController.largeTitleTextColor.resolvedColor(with: UITraitCollection(userInterfaceStyle: .light))
titleView.attributedText = page.title
titleView.adjustsFontForContentSizeCategory = true
titleView.numberOfLines = 0
label = UILabel()
label.text = page.content
label.font = WelcomeViewController.subTitleFont
label.textColor = WelcomeViewController.largeTitleTextColor.resolvedColor(with: UITraitCollection(userInterfaceStyle: .light))
label.adjustsFontForContentSizeCategory = true
@ -43,7 +42,7 @@ class WelcomeContentPageView: UIView {
blurryBackgroundView.contentView.addSubview(contentStackView)
super.init(frame: .zero)
super.init(frame: frame)
addSubview(blurryBackgroundView)
@ -55,16 +54,21 @@ class WelcomeContentPageView: UIView {
private func setupConstraints() {
let constraints = [
blurryBackgroundView.topAnchor.constraint(equalTo: topAnchor),
blurryBackgroundView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
trailingAnchor.constraint(equalTo: blurryBackgroundView.trailingAnchor, constant: 16),
blurryBackgroundView.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingAnchor.constraint(equalTo: blurryBackgroundView.trailingAnchor),
bottomAnchor.constraint(greaterThanOrEqualTo: blurryBackgroundView.bottomAnchor),
contentStackView.topAnchor.constraint(equalTo: blurryBackgroundView.contentView.topAnchor, constant: 8),
contentStackView.leadingAnchor.constraint(equalTo: blurryBackgroundView.contentView.leadingAnchor, constant: 8),
blurryBackgroundView.contentView.trailingAnchor.constraint(equalTo: contentStackView.trailingAnchor, constant: 8),
blurryBackgroundView.contentView.bottomAnchor.constraint(equalTo: contentStackView.bottomAnchor, constant: 8),
blurryBackgroundView.contentView.trailingAnchor.constraint(equalTo: contentStackView.trailingAnchor),
blurryBackgroundView.contentView.bottomAnchor.constraint(equalTo: contentStackView.bottomAnchor),
]
NSLayoutConstraint.activate(constraints)
}
func update(with page: WelcomeContentPage) {
titleView.attributedText = page.title
label.text = page.content
}
}

View File

@ -1,28 +0,0 @@
//
// WelcomeContentViewController.swift
// Mastodon
//
// Created by Nathan Mattes on 26.11.22.
//
import UIKit
class WelcomeContentViewController: UIViewController {
let page: WelcomeContentPage
var contentView: WelcomeContentPageView {
view as! WelcomeContentPageView
}
init(page: WelcomeContentPage) {
self.page = page
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
let pageView = WelcomeContentPageView(page: page)
self.view = pageView
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}

View File

@ -29,6 +29,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
private(set) lazy var dismissBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(WelcomeViewController.dismissBarButtonItemDidPressed(_:)))
let buttonContainer = UIStackView()
let educationPages: [WelcomeContentPage] = [.whatIsMastodon, .mastodonIsLikeThat, .howDoIPickAServer]
private(set) lazy var signUpButton: PrimaryActionButton = {
let button = PrimaryActionButton()
@ -58,13 +59,24 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
return button
}()
private(set) lazy var pageViewController: UIPageViewController = {
let pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
pageController.setViewControllers([WelcomeContentViewController(page: .whatIsMastodon)], direction: .forward, animated: false)
return pageController
private(set) lazy var pageCollectionView: UICollectionView = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
//FIXME: cell-size.
flowLayout.itemSize = CGSize(width: self.view.frame.width, height: 300)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.isPagingEnabled = true
collectionView.backgroundColor = nil
collectionView.showsHorizontalScrollIndicator = false
collectionView.bounces = false
collectionView.register(WelcomeContentCollectionViewCell.self, forCellWithReuseIdentifier: WelcomeContentCollectionViewCell.identifier)
return collectionView
}()
var currentPage: WelcomeContentPage = .whatIsMastodon
var currentPageOffset = 0
}
extension WelcomeViewController {
@ -121,24 +133,18 @@ extension WelcomeViewController {
signUpButton.addTarget(self, action: #selector(signUpButtonDidClicked(_:)), for: .touchUpInside)
signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside)
pageViewController.delegate = self
pageViewController.dataSource = self
addChild(pageViewController)
pageViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pageViewController.view)
pageViewController.didMove(toParent: self)
let scrollviews = pageViewController.view.subviews.filter { type(of: $0).isSubclass(of: UIScrollView.self) }.compactMap { $0 as? UIScrollView }
for scrollView in scrollviews {
scrollView.delegate = self
}
pageCollectionView.delegate = self
pageCollectionView.dataSource = self
view.addSubview(pageCollectionView)
let scrollView = pageCollectionView as UIScrollView
scrollView.delegate = self
NSLayoutConstraint.activate([
pageViewController.view.topAnchor.constraint(equalTo: view.topAnchor, constant: computedTopAnchorInset),
pageViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: pageViewController.view.trailingAnchor),
buttonContainer.topAnchor.constraint(equalTo: pageViewController.view.bottomAnchor, constant: 16),
pageCollectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: computedTopAnchorInset),
pageCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: pageCollectionView.trailingAnchor),
buttonContainer.topAnchor.constraint(equalTo: pageCollectionView.bottomAnchor, constant: 16),
])
viewModel.$needsShowDismissEntry
@ -287,70 +293,29 @@ extension WelcomeViewController: UIAdaptivePresentationControllerDelegate {
}
}
//MARK: - UIPageViewControllerDelegate
extension WelcomeViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let currentViewController = pageViewController.viewControllers?.first as? WelcomeContentViewController else { return }
currentPage = currentViewController.page
if let pageIndex = WelcomeContentPage.allCases.firstIndex(of: currentPage) {
let offset = Int(pageIndex) * Int(pageViewController.view.frame.width)
currentPageOffset = offset
welcomeIllustrationView.update(contentOffset: CGFloat(offset))
}
}
}
//MARK: - UIPageViewDataSource
extension WelcomeViewController: UIPageViewControllerDataSource {
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
WelcomeContentPage.allCases.firstIndex(of: currentPage) ?? 0
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return WelcomeContentPage.allCases.count
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewController = viewController as? WelcomeContentViewController else { return nil }
let currentPage = viewController.page
switch currentPage {
case .whatIsMastodon:
return nil
case .mastodonIsLikeThat:
return WelcomeContentViewController(page: .whatIsMastodon)
case .howDoIPickAServer:
return WelcomeContentViewController(page: .mastodonIsLikeThat)
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewController = viewController as? WelcomeContentViewController else { return nil }
let currentPage = viewController.page
switch currentPage {
case .whatIsMastodon:
return WelcomeContentViewController(page: .mastodonIsLikeThat)
case .mastodonIsLikeThat:
return WelcomeContentViewController(page: .howDoIPickAServer)
case .howDoIPickAServer:
return nil
}
}
}
//MARK: - UIScrollViewDelegate
extension WelcomeViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let weirdScrollViewJumpingCorrectionFactor = pageViewController.view.frame.width
let contentOffset = CGFloat(currentPageOffset) + scrollView.contentOffset.x - weirdScrollViewJumpingCorrectionFactor
let contentOffset = scrollView.contentOffset.x
welcomeIllustrationView.update(contentOffset: contentOffset)
}
}
//MARK: - UICollectionViewDelegate
extension WelcomeViewController: UICollectionViewDelegate { }
//MARK: - UICollectionViewDataSource
extension WelcomeViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
educationPages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WelcomeContentCollectionViewCell.identifier, for: indexPath) as? WelcomeContentCollectionViewCell else { fatalError("WTF? Wrong cell?") }
let page = educationPages[indexPath.item]
cell.update(with: page)
return cell
}
}