diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 6eda0f45..f6154955 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -286,6 +286,7 @@ D6CA6ED229EF6091003EC5DF /* TuskerPreferences in Frameworks */ = {isa = PBXBuildFile; productRef = D6CA6ED129EF6091003EC5DF /* TuskerPreferences */; }; D6CA8CDA2962231F0050C433 /* ArrayUniqueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */; }; D6CA8CDE296387310050C433 /* SaveToPhotosActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */; }; + D6CF5B832AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CF5B822AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift */; }; D6D12B2F2925D66500D528E1 /* TimelineGapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */; }; D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */; }; D6D12B58292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B57292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift */; }; @@ -685,6 +686,7 @@ D6CA6ED029EF6060003EC5DF /* TuskerPreferences */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TuskerPreferences; path = Packages/TuskerPreferences; sourceTree = ""; }; D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUniqueTests.swift; sourceTree = ""; }; D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveToPhotosActivity.swift; sourceTree = ""; }; + D6CF5B822AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSCollectionLayoutSection+Readable.swift"; sourceTree = ""; }; D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineGapCollectionViewCell.swift; sourceTree = ""; }; D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCollectionViewCell.swift; sourceTree = ""; }; D6D12B57292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListCollectionViewController.swift; sourceTree = ""; }; @@ -1245,6 +1247,7 @@ D61F75AC293AF39000C0B37F /* Filter+Helpers.swift */, D6C3F5162991C1A00009FCFF /* View+AppListStyle.swift */, D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */, + D6CF5B822AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift */, ); path = Extensions; sourceTree = ""; @@ -2218,6 +2221,7 @@ D6C3F5172991C1A00009FCFF /* View+AppListStyle.swift in Sources */, D65B4B6A297777D900DABDFB /* StatusNotFoundView.swift in Sources */, D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */, + D6CF5B832AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift in Sources */, D601FA5D297B2E6F00A8E8B5 /* ConversationCollectionViewController.swift in Sources */, D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */, D60088F22980DAA0005B4D00 /* TipJarView.swift in Sources */, diff --git a/Tusker/Extensions/NSCollectionLayoutSection+Readable.swift b/Tusker/Extensions/NSCollectionLayoutSection+Readable.swift new file mode 100644 index 00000000..da50e671 --- /dev/null +++ b/Tusker/Extensions/NSCollectionLayoutSection+Readable.swift @@ -0,0 +1,45 @@ +// +// NSCollectionLayoutSection+Readable.swift +// Tusker +// +// Created by Shadowfacts on 9/28/23. +// Copyright © 2023 Shadowfacts. All rights reserved. +// + +import UIKit + +extension NSCollectionLayoutSection { + + // The .readableContent insets reference has a bunch of weird behavior, + // so just calculate the content inset ourselves. + func readableContentInset(in environment: NSCollectionLayoutEnvironment) { + let maxWidth: CGFloat + switch environment.traitCollection.preferredContentSizeCategory { + case .extraSmall: + maxWidth = 560 + case .small: + maxWidth = 600 + case .medium: + maxWidth = 632 + case .large: + maxWidth = 664 + case .extraLarge: + maxWidth = 744 + case .extraExtraLarge: + maxWidth = 816 + case .extraExtraExtraLarge: + maxWidth = 896 + case .accessibilityMedium: + maxWidth = 1096 + default: + // greater accessibility sizes don't have a limit + return + } + + let inset = max(0, (environment.container.contentSize.width - maxWidth) / 2) + // make sure not to overwrite the vertical insets, which are non-zero for grouped styles + contentInsets.leading = inset + contentInsets.trailing = inset + } + +} diff --git a/Tusker/Screens/Account Follows/AccountFollowsListViewController.swift b/Tusker/Screens/Account Follows/AccountFollowsListViewController.swift index 15615f35..f211bbfd 100644 --- a/Tusker/Screens/Account Follows/AccountFollowsListViewController.swift +++ b/Tusker/Screens/Account Follows/AccountFollowsListViewController.swift @@ -56,9 +56,7 @@ class AccountFollowsListViewController: UIViewController, CollectionViewControll } let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } view = UICollectionView(frame: .zero, collectionViewLayout: layout) diff --git a/Tusker/Screens/Account List/AccountListViewController.swift b/Tusker/Screens/Account List/AccountListViewController.swift index 8fe63a98..6135755e 100644 --- a/Tusker/Screens/Account List/AccountListViewController.swift +++ b/Tusker/Screens/Account List/AccountListViewController.swift @@ -35,9 +35,7 @@ class AccountListViewController: UIViewController, CollectionViewController { config.backgroundColor = .appGroupedBackground let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } view = UICollectionView(frame: .zero, collectionViewLayout: layout) diff --git a/Tusker/Screens/Conversation/ConversationCollectionViewController.swift b/Tusker/Screens/Conversation/ConversationCollectionViewController.swift index 5abdbece..c0b68430 100644 --- a/Tusker/Screens/Conversation/ConversationCollectionViewController.swift +++ b/Tusker/Screens/Conversation/ConversationCollectionViewController.swift @@ -59,10 +59,12 @@ class ConversationCollectionViewController: UIViewController, CollectionViewCont config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets return config } - // we're not using contenetInsetsReference = .readableContent here because it always insets the cells even if - // the collection view's actual width is narrow enough to fit in the readable width, resulting in a bit of the - // background color always peeking through the edges - let layout = UICollectionViewCompositionalLayout.list(using: config) + let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in + let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) + section.readableContentInset(in: environment) + return section + } + viewRespectsSystemMinimumLayoutMargins = false view = UICollectionView(frame: .zero, collectionViewLayout: layout) // something about the autoresizing mask breaks resizing the vc view.translatesAutoresizingMaskIntoConstraints = false diff --git a/Tusker/Screens/Explore/TrendingStatusesViewController.swift b/Tusker/Screens/Explore/TrendingStatusesViewController.swift index e3a6201e..447054ae 100644 --- a/Tusker/Screens/Explore/TrendingStatusesViewController.swift +++ b/Tusker/Screens/Explore/TrendingStatusesViewController.swift @@ -62,9 +62,7 @@ class TrendingStatusesViewController: UIViewController, CollectionViewController } let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } view = UICollectionView(frame: .zero, collectionViewLayout: layout) diff --git a/Tusker/Screens/Local Predicate Statuses List/LocalPredicateStatusesViewController.swift b/Tusker/Screens/Local Predicate Statuses List/LocalPredicateStatusesViewController.swift index 665af8d4..a28b4059 100644 --- a/Tusker/Screens/Local Predicate Statuses List/LocalPredicateStatusesViewController.swift +++ b/Tusker/Screens/Local Predicate Statuses List/LocalPredicateStatusesViewController.swift @@ -68,9 +68,7 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont } let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } view = UICollectionView(frame: .zero, collectionViewLayout: layout) diff --git a/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift b/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift index bc41622a..ce7433c8 100644 --- a/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift +++ b/Tusker/Screens/Notifications/NotificationsCollectionViewController.swift @@ -93,9 +93,7 @@ class NotificationsCollectionViewController: UIViewController, TimelineLikeColle } let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) diff --git a/Tusker/Screens/Profile/ProfileStatusesViewController.swift b/Tusker/Screens/Profile/ProfileStatusesViewController.swift index 0afde88d..cf85c9fa 100644 --- a/Tusker/Screens/Profile/ProfileStatusesViewController.swift +++ b/Tusker/Screens/Profile/ProfileStatusesViewController.swift @@ -91,9 +91,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie return .list(using: config, layoutEnvironment: environment) } else { let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } } diff --git a/Tusker/Screens/Status Action Account List/StatusActionAccountListCollectionViewController.swift b/Tusker/Screens/Status Action Account List/StatusActionAccountListCollectionViewController.swift index 409ef835..55c32056 100644 --- a/Tusker/Screens/Status Action Account List/StatusActionAccountListCollectionViewController.swift +++ b/Tusker/Screens/Status Action Account List/StatusActionAccountListCollectionViewController.swift @@ -60,6 +60,7 @@ class StatusActionAccountListCollectionViewController: UIViewController, Collect return config } let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in + let section: NSCollectionLayoutSection switch dataSource.sectionIdentifier(for: sectionIndex)! { case .status: var config = UICollectionLayoutListConfiguration(appearance: .grouped) @@ -71,14 +72,12 @@ class StatusActionAccountListCollectionViewController: UIViewController, Collect config.trailingSwipeActionsConfigurationProvider = { [unowned self] in (collectionView.cellForItem(at: $0) as? TimelineStatusCollectionViewCell)?.trailingSwipeActions() } - let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } - return section + section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) case .accounts: - return NSCollectionLayoutSection.list(using: accountsConfig, layoutEnvironment: environment) + section = NSCollectionLayoutSection.list(using: accountsConfig, layoutEnvironment: environment) } + section.readableContentInset(in: environment) + return section } view = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.delegate = self diff --git a/Tusker/Screens/Status Edit History/StatusEditHistoryViewController.swift b/Tusker/Screens/Status Edit History/StatusEditHistoryViewController.swift index 10c957aa..8a672a14 100644 --- a/Tusker/Screens/Status Edit History/StatusEditHistoryViewController.swift +++ b/Tusker/Screens/Status Edit History/StatusEditHistoryViewController.swift @@ -54,9 +54,7 @@ class StatusEditHistoryViewController: UIViewController, CollectionViewControlle } let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) diff --git a/Tusker/Screens/Timeline/TimelineViewController.swift b/Tusker/Screens/Timeline/TimelineViewController.swift index 14c42986..f27349f2 100644 --- a/Tusker/Screens/Timeline/TimelineViewController.swift +++ b/Tusker/Screens/Timeline/TimelineViewController.swift @@ -109,12 +109,10 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro } return config } - // just setting layout.configuration.contentInsetsReference doesn't work with UICollectionViewCompositionalLayout.list + // Use the sectionProvider closure, because the content inset depends on the environment. let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment) - if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { - section.contentInsetsReference = .readableContent - } + section.readableContentInset(in: environment) return section } collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)