Compare commits
No commits in common. "31a0db014a989bbcd2464299c14e081ff311c88b" and "0948371f838cb0bba494381cdfae9c42bd2e0710" have entirely different histories.
31a0db014a
...
0948371f83
|
@ -286,8 +286,6 @@
|
||||||
D6CA6ED229EF6091003EC5DF /* TuskerPreferences in Frameworks */ = {isa = PBXBuildFile; productRef = D6CA6ED129EF6091003EC5DF /* TuskerPreferences */; };
|
D6CA6ED229EF6091003EC5DF /* TuskerPreferences in Frameworks */ = {isa = PBXBuildFile; productRef = D6CA6ED129EF6091003EC5DF /* TuskerPreferences */; };
|
||||||
D6CA8CDA2962231F0050C433 /* ArrayUniqueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */; };
|
D6CA8CDA2962231F0050C433 /* ArrayUniqueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */; };
|
||||||
D6CA8CDE296387310050C433 /* SaveToPhotosActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA8CDD296387310050C433 /* SaveToPhotosActivity.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 */; };
|
|
||||||
D6CF5B852AC7C56F00F15D83 /* MultiColumnCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CF5B842AC7C56F00F15D83 /* MultiColumnCollectionViewLayout.swift */; };
|
|
||||||
D6D12B2F2925D66500D528E1 /* TimelineGapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */; };
|
D6D12B2F2925D66500D528E1 /* TimelineGapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */; };
|
||||||
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */; };
|
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */; };
|
||||||
D6D12B58292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B57292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift */; };
|
D6D12B58292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12B57292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift */; };
|
||||||
|
@ -687,8 +685,6 @@
|
||||||
D6CA6ED029EF6060003EC5DF /* TuskerPreferences */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TuskerPreferences; path = Packages/TuskerPreferences; sourceTree = "<group>"; };
|
D6CA6ED029EF6060003EC5DF /* TuskerPreferences */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TuskerPreferences; path = Packages/TuskerPreferences; sourceTree = "<group>"; };
|
||||||
D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUniqueTests.swift; sourceTree = "<group>"; };
|
D6CA8CD92962231F0050C433 /* ArrayUniqueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUniqueTests.swift; sourceTree = "<group>"; };
|
||||||
D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveToPhotosActivity.swift; sourceTree = "<group>"; };
|
D6CA8CDD296387310050C433 /* SaveToPhotosActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveToPhotosActivity.swift; sourceTree = "<group>"; };
|
||||||
D6CF5B822AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSCollectionLayoutSection+Readable.swift"; sourceTree = "<group>"; };
|
|
||||||
D6CF5B842AC7C56F00F15D83 /* MultiColumnCollectionViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiColumnCollectionViewLayout.swift; sourceTree = "<group>"; };
|
|
||||||
D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineGapCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6D12B2E2925D66500D528E1 /* TimelineGapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineGapCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6D12B55292D57E800D528E1 /* AccountCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6D12B57292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListCollectionViewController.swift; sourceTree = "<group>"; };
|
D6D12B57292D5B2C00D528E1 /* StatusActionAccountListCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListCollectionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1249,7 +1245,6 @@
|
||||||
D61F75AC293AF39000C0B37F /* Filter+Helpers.swift */,
|
D61F75AC293AF39000C0B37F /* Filter+Helpers.swift */,
|
||||||
D6C3F5162991C1A00009FCFF /* View+AppListStyle.swift */,
|
D6C3F5162991C1A00009FCFF /* View+AppListStyle.swift */,
|
||||||
D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */,
|
D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */,
|
||||||
D6CF5B822AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift */,
|
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1504,7 +1499,6 @@
|
||||||
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
|
||||||
D61DC84528F498F200B82C6E /* Logging.swift */,
|
D61DC84528F498F200B82C6E /* Logging.swift */,
|
||||||
D6B81F432560390300F6E31D /* MenuController.swift */,
|
D6B81F432560390300F6E31D /* MenuController.swift */,
|
||||||
D6CF5B842AC7C56F00F15D83 /* MultiColumnCollectionViewLayout.swift */,
|
|
||||||
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */,
|
||||||
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */,
|
||||||
D61F759E29385AD800C0B37F /* SemiCaseSensitiveComparator.swift */,
|
D61F759E29385AD800C0B37F /* SemiCaseSensitiveComparator.swift */,
|
||||||
|
@ -2084,7 +2078,6 @@
|
||||||
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */,
|
D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */,
|
||||||
D6CA8CDE296387310050C433 /* SaveToPhotosActivity.swift in Sources */,
|
D6CA8CDE296387310050C433 /* SaveToPhotosActivity.swift in Sources */,
|
||||||
D6B936712829F72900237D0E /* NSManagedObjectContext+Helpers.swift in Sources */,
|
D6B936712829F72900237D0E /* NSManagedObjectContext+Helpers.swift in Sources */,
|
||||||
D6CF5B852AC7C56F00F15D83 /* MultiColumnCollectionViewLayout.swift in Sources */,
|
|
||||||
D68A76EE295369C7001DA1B3 /* AcknowledgementsView.swift in Sources */,
|
D68A76EE295369C7001DA1B3 /* AcknowledgementsView.swift in Sources */,
|
||||||
D6C3F5192991F5D60009FCFF /* TimelineJumpButton.swift in Sources */,
|
D6C3F5192991F5D60009FCFF /* TimelineJumpButton.swift in Sources */,
|
||||||
D63CC7102911F1E4000E19DE /* UIScrollView+Top.swift in Sources */,
|
D63CC7102911F1E4000E19DE /* UIScrollView+Top.swift in Sources */,
|
||||||
|
@ -2225,7 +2218,6 @@
|
||||||
D6C3F5172991C1A00009FCFF /* View+AppListStyle.swift in Sources */,
|
D6C3F5172991C1A00009FCFF /* View+AppListStyle.swift in Sources */,
|
||||||
D65B4B6A297777D900DABDFB /* StatusNotFoundView.swift in Sources */,
|
D65B4B6A297777D900DABDFB /* StatusNotFoundView.swift in Sources */,
|
||||||
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
D6412B0924B0291E00F5412E /* MyProfileViewController.swift in Sources */,
|
||||||
D6CF5B832AC65DDF00F15D83 /* NSCollectionLayoutSection+Readable.swift in Sources */,
|
|
||||||
D601FA5D297B2E6F00A8E8B5 /* ConversationCollectionViewController.swift in Sources */,
|
D601FA5D297B2E6F00A8E8B5 /* ConversationCollectionViewController.swift in Sources */,
|
||||||
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */,
|
D6D12B56292D57E800D528E1 /* AccountCollectionViewCell.swift in Sources */,
|
||||||
D60088F22980DAA0005B4D00 /* TipJarView.swift in Sources */,
|
D60088F22980DAA0005B4D00 /* TipJarView.swift in Sources */,
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
//
|
|
||||||
// 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) {
|
|
||||||
guard let maximumReadableWidth = environment.maximumReadableWidth else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let inset = max(0, (environment.container.contentSize.width - maximumReadableWidth) / 2)
|
|
||||||
// make sure not to overwrite the vertical insets, which are non-zero for grouped styles
|
|
||||||
contentInsets.leading = inset
|
|
||||||
contentInsets.trailing = inset
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NSCollectionLayoutEnvironment {
|
|
||||||
var maximumReadableWidth: CGFloat? {
|
|
||||||
switch traitCollection.preferredContentSizeCategory {
|
|
||||||
case .extraSmall:
|
|
||||||
return 560
|
|
||||||
case .small:
|
|
||||||
return 600
|
|
||||||
case .medium:
|
|
||||||
return 632
|
|
||||||
case .large:
|
|
||||||
return 664
|
|
||||||
case .extraLarge:
|
|
||||||
return 744
|
|
||||||
case .extraExtraLarge:
|
|
||||||
return 816
|
|
||||||
case .extraExtraExtraLarge:
|
|
||||||
return 896
|
|
||||||
case .accessibilityMedium:
|
|
||||||
return 1096
|
|
||||||
default:
|
|
||||||
// greater accessibility sizes don't have a limit
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,260 +0,0 @@
|
||||||
//
|
|
||||||
// MultiColumnCollectionViewLayout.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 9/29/23.
|
|
||||||
// Copyright © 2023 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class MultiColumnCollectionViewLayout: UICollectionViewLayout {
|
|
||||||
|
|
||||||
private let numberOfColumns: Int
|
|
||||||
private let spacing: CGFloat
|
|
||||||
private let minimumColumnWidth: CGFloat
|
|
||||||
private var effectiveNumberOfColumns: Int!
|
|
||||||
|
|
||||||
private var attributes: [MultiColumnLayoutAttributes] = []
|
|
||||||
private var invalidatedItemIndices: IndexSet = []
|
|
||||||
|
|
||||||
var showSectionHeader = false {
|
|
||||||
didSet {
|
|
||||||
if showSectionHeader != oldValue {
|
|
||||||
invalidateLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private var sectionHeaderAttributes: MultiColumnLayoutAttributes?
|
|
||||||
|
|
||||||
init(numberOfColumns: Int, columnSpacing: CGFloat, minimumColumnWidth: CGFloat) {
|
|
||||||
precondition(numberOfColumns >= 1)
|
|
||||||
self.numberOfColumns = numberOfColumns
|
|
||||||
self.effectiveNumberOfColumns = nil
|
|
||||||
self.spacing = columnSpacing
|
|
||||||
self.minimumColumnWidth = minimumColumnWidth
|
|
||||||
super.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override class var layoutAttributesClass: AnyClass {
|
|
||||||
MultiColumnLayoutAttributes.self
|
|
||||||
}
|
|
||||||
|
|
||||||
override class var invalidationContextClass: AnyClass {
|
|
||||||
MultiColumnLayoutInvalidationContext.self
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepare() {
|
|
||||||
guard let collectionView else { return }
|
|
||||||
precondition(collectionView.numberOfSections <= 1)
|
|
||||||
|
|
||||||
guard collectionView.numberOfSections == 1 else {
|
|
||||||
attributes = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if effectiveNumberOfColumns == nil {
|
|
||||||
updateEffectiveNumberOfColumns()
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastAttributesInEachColumn: [MultiColumnLayoutAttributes?] = Array(repeating: nil, count: effectiveNumberOfColumns)
|
|
||||||
func columnWithMinHeight() -> Int {
|
|
||||||
var min: Int?
|
|
||||||
for i in 0..<effectiveNumberOfColumns {
|
|
||||||
guard let attrs = lastAttributesInEachColumn[i] else {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
if min == nil || attrs.frame.maxY < lastAttributesInEachColumn[min!]!.frame.maxY {
|
|
||||||
min = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return min!
|
|
||||||
}
|
|
||||||
|
|
||||||
let columnWidth = columnWidthForColumnCount(effectiveNumberOfColumns)
|
|
||||||
func minXForColumn(_ column: Int) -> CGFloat {
|
|
||||||
(CGFloat(column) * columnWidth) + ((CGFloat(column) + 1) * spacing)
|
|
||||||
}
|
|
||||||
|
|
||||||
let startY: CGFloat
|
|
||||||
if showSectionHeader {
|
|
||||||
let indexPath = IndexPath(item: 0, section: 0)
|
|
||||||
if sectionHeaderAttributes == nil {
|
|
||||||
sectionHeaderAttributes = .init(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: indexPath)
|
|
||||||
}
|
|
||||||
sectionHeaderAttributes!.frame = CGRect(x: 0, y: 0, width: collectionView.bounds.width, height: 0)
|
|
||||||
if let view = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionHeader, at: indexPath) {
|
|
||||||
let preferred = view.preferredLayoutAttributesFitting(sectionHeaderAttributes!)
|
|
||||||
sectionHeaderAttributes!.frame.size.height = preferred.frame.height
|
|
||||||
}
|
|
||||||
startY = sectionHeaderAttributes!.frame.height
|
|
||||||
} else {
|
|
||||||
startY = spacing
|
|
||||||
}
|
|
||||||
|
|
||||||
let numberOfItems = collectionView.numberOfItems(inSection: 0)
|
|
||||||
for item in 0..<numberOfItems {
|
|
||||||
let column = columnWithMinHeight()
|
|
||||||
|
|
||||||
let itemAttrs: MultiColumnLayoutAttributes
|
|
||||||
if attributes.count > item {
|
|
||||||
itemAttrs = attributes[item]
|
|
||||||
} else {
|
|
||||||
itemAttrs = MultiColumnLayoutAttributes(forCellWith: IndexPath(item: item, section: 0))
|
|
||||||
// estimate
|
|
||||||
itemAttrs.frame.size.height = 200
|
|
||||||
attributes.append(itemAttrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
itemAttrs.column = column
|
|
||||||
itemAttrs.frame.size.width = columnWidth
|
|
||||||
|
|
||||||
if invalidatedItemIndices.contains(item) {
|
|
||||||
if let cell = collectionView.cellForItem(at: IndexPath(item: item, section: 0)) {
|
|
||||||
let preferred = cell.preferredLayoutAttributesFitting(itemAttrs.copy() as! UICollectionViewLayoutAttributes)
|
|
||||||
itemAttrs.frame.size.height = preferred.frame.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let lastInColumn = lastAttributesInEachColumn[column] {
|
|
||||||
itemAttrs.frame.origin = CGPoint(
|
|
||||||
x: lastInColumn.frame.minX,
|
|
||||||
y: lastInColumn.frame.maxY + spacing
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
itemAttrs.frame.origin = CGPoint(
|
|
||||||
x: minXForColumn(column),
|
|
||||||
y: startY
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastAttributesInEachColumn[column] = itemAttrs
|
|
||||||
}
|
|
||||||
|
|
||||||
if attributes.count > numberOfItems {
|
|
||||||
attributes.removeLast(attributes.count - numberOfItems)
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidatedItemIndices = []
|
|
||||||
}
|
|
||||||
|
|
||||||
override var collectionViewContentSize: CGSize {
|
|
||||||
guard let collectionView else {
|
|
||||||
return .zero
|
|
||||||
}
|
|
||||||
let maxY = attributes.lazy.map(\.frame.maxY).max()
|
|
||||||
return CGSize(width: collectionView.bounds.width, height: (maxY ?? 0) + spacing)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
|
||||||
return attributes[indexPath.item]
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
|
||||||
// TODO: optimize this
|
|
||||||
var attributes = attributes.filter { $0.frame.intersects(rect) }
|
|
||||||
if showSectionHeader,
|
|
||||||
let sectionHeaderAttributes,
|
|
||||||
rect.minY <= sectionHeaderAttributes.frame.maxY {
|
|
||||||
attributes.append(sectionHeaderAttributes)
|
|
||||||
}
|
|
||||||
return attributes
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
|
||||||
if elementKind == UICollectionView.elementKindSectionHeader,
|
|
||||||
showSectionHeader {
|
|
||||||
return sectionHeaderAttributes
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func indexPathsToInsertForSupplementaryView(ofKind elementKind: String) -> [IndexPath] {
|
|
||||||
if elementKind == UICollectionView.elementKindSectionHeader {
|
|
||||||
return [IndexPath(item: 0, section: 0)]
|
|
||||||
} else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
|
|
||||||
return newBounds.width != collectionView?.bounds.width
|
|
||||||
}
|
|
||||||
|
|
||||||
override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
|
|
||||||
let context = super.invalidationContext(forBoundsChange: newBounds) as! MultiColumnLayoutInvalidationContext
|
|
||||||
context.updateEffectiveNumberOfColumns = true
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
|
|
||||||
// On landscape-to-portrait device rotations (though seemingly only when inside a UISplitViewController), the collection view
|
|
||||||
// doesn't go through the bounds-change codepath. But we may still need to update the effective number of columns,
|
|
||||||
// so override this private method (which is called) and configure the invalidation context appropriately.
|
|
||||||
override func _invalidationContext(forUpdatedLayoutMargins margins: UIEdgeInsets) -> UICollectionViewLayoutInvalidationContext {
|
|
||||||
let context = super._invalidationContext(forUpdatedLayoutMargins: margins) as! MultiColumnLayoutInvalidationContext
|
|
||||||
context.updateEffectiveNumberOfColumns = true
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
|
|
||||||
override func shouldInvalidateLayout(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool {
|
|
||||||
return preferredAttributes.frame.height != originalAttributes.frame.height
|
|
||||||
}
|
|
||||||
|
|
||||||
override func invalidationContext(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutInvalidationContext {
|
|
||||||
let context = super.invalidationContext(forPreferredLayoutAttributes: preferredAttributes, withOriginalAttributes: originalAttributes)
|
|
||||||
if let kind = preferredAttributes.representedElementKind {
|
|
||||||
context.invalidateSupplementaryElements(ofKind: kind, at: [preferredAttributes.indexPath])
|
|
||||||
} else {
|
|
||||||
context.invalidateItems(at: [preferredAttributes.indexPath])
|
|
||||||
}
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
|
|
||||||
override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) {
|
|
||||||
let context = context as! MultiColumnLayoutInvalidationContext
|
|
||||||
|
|
||||||
for indexPath in context.invalidatedItemIndexPaths ?? [] {
|
|
||||||
invalidatedItemIndices.insert(indexPath.item)
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.invalidateEverything || context.updateEffectiveNumberOfColumns {
|
|
||||||
updateEffectiveNumberOfColumns()
|
|
||||||
}
|
|
||||||
|
|
||||||
super.invalidateLayout(with: context)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func columnWidthForColumnCount(_ count: Int) -> CGFloat {
|
|
||||||
guard let collectionView else { return 0 }
|
|
||||||
let spacingTotal = spacing * (CGFloat(count) + 1)
|
|
||||||
return (collectionView.bounds.width - spacingTotal) / CGFloat(count)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateEffectiveNumberOfColumns() {
|
|
||||||
var n = numberOfColumns
|
|
||||||
while n > 1 && columnWidthForColumnCount(n) < minimumColumnWidth {
|
|
||||||
n -= 1
|
|
||||||
}
|
|
||||||
effectiveNumberOfColumns = n
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MultiColumnLayoutAttributes: UICollectionViewLayoutAttributes {
|
|
||||||
var column: Int = -1
|
|
||||||
|
|
||||||
override func copy(with zone: NSZone? = nil) -> Any {
|
|
||||||
let copy = super.copy(with: zone) as! MultiColumnLayoutAttributes
|
|
||||||
copy.column = column
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MultiColumnLayoutInvalidationContext: UICollectionViewLayoutInvalidationContext {
|
|
||||||
var updateEffectiveNumberOfColumns: Bool = false
|
|
||||||
}
|
|
|
@ -56,7 +56,9 @@ class AccountFollowsListViewController: UIViewController, CollectionViewControll
|
||||||
}
|
}
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
|
|
@ -35,7 +35,9 @@ class AccountListViewController: UIViewController, CollectionViewController {
|
||||||
config.backgroundColor = .appGroupedBackground
|
config.backgroundColor = .appGroupedBackground
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
|
|
@ -59,12 +59,10 @@ class ConversationCollectionViewController: UIViewController, CollectionViewCont
|
||||||
config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
|
config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
// we're not using contenetInsetsReference = .readableContent here because it always insets the cells even if
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
// the collection view's actual width is narrow enough to fit in the readable width, resulting in a bit of the
|
||||||
section.readableContentInset(in: environment)
|
// background color always peeking through the edges
|
||||||
return section
|
let layout = UICollectionViewCompositionalLayout.list(using: config)
|
||||||
}
|
|
||||||
viewRespectsSystemMinimumLayoutMargins = false
|
|
||||||
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
// something about the autoresizing mask breaks resizing the vc
|
// something about the autoresizing mask breaks resizing the vc
|
||||||
view.translatesAutoresizingMaskIntoConstraints = false
|
view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
|
@ -14,7 +14,6 @@ class SuggestedProfilesViewController: UIViewController, CollectionViewControlle
|
||||||
weak var mastodonController: MastodonController!
|
weak var mastodonController: MastodonController!
|
||||||
|
|
||||||
var collectionView: UICollectionView!
|
var collectionView: UICollectionView!
|
||||||
private var layout: MultiColumnCollectionViewLayout!
|
|
||||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||||
|
|
||||||
private var state = State.unloaded
|
private var state = State.unloaded
|
||||||
|
@ -34,7 +33,30 @@ class SuggestedProfilesViewController: UIViewController, CollectionViewControlle
|
||||||
|
|
||||||
title = "Suggested Accounts"
|
title = "Suggested Accounts"
|
||||||
|
|
||||||
layout = MultiColumnCollectionViewLayout(numberOfColumns: 2, columnSpacing: 16, minimumColumnWidth: 320)
|
let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in
|
||||||
|
switch dataSource.sectionIdentifier(for: sectionIndex) {
|
||||||
|
case nil:
|
||||||
|
fatalError()
|
||||||
|
|
||||||
|
case .loadingIndicator:
|
||||||
|
var config = UICollectionLayoutListConfiguration(appearance: .grouped)
|
||||||
|
config.backgroundColor = .appGroupedBackground
|
||||||
|
config.showsSeparators = false
|
||||||
|
return .list(using: config, layoutEnvironment: environment)
|
||||||
|
|
||||||
|
case .accounts:
|
||||||
|
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(280))
|
||||||
|
let item = NSCollectionLayoutItem(layoutSize: size)
|
||||||
|
let item2 = NSCollectionLayoutItem(layoutSize: size)
|
||||||
|
let group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitems: [item, item2])
|
||||||
|
group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
|
||||||
|
group.interItemSpacing = .fixed(16)
|
||||||
|
let section = NSCollectionLayoutSection(group: group)
|
||||||
|
section.interGroupSpacing = 16
|
||||||
|
section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 0)
|
||||||
|
return section
|
||||||
|
}
|
||||||
|
}
|
||||||
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
|
@ -53,27 +75,21 @@ class SuggestedProfilesViewController: UIViewController, CollectionViewControlle
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||||
|
let loadingCell = UICollectionView.CellRegistration<LoadingCollectionViewCell, Void> { cell, indexPath, itemIdentifier in
|
||||||
|
cell.indicator.startAnimating()
|
||||||
|
}
|
||||||
let accountCell = UICollectionView.CellRegistration<SuggestedProfileCardCollectionViewCell, (String, Suggestion.Source)>(cellNib: UINib(nibName: "SuggestedProfileCardCollectionViewCell", bundle: .main)) { cell, indexPath, item in
|
let accountCell = UICollectionView.CellRegistration<SuggestedProfileCardCollectionViewCell, (String, Suggestion.Source)>(cellNib: UINib(nibName: "SuggestedProfileCardCollectionViewCell", bundle: .main)) { cell, indexPath, item in
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
cell.updateUI(accountID: item.0, source: item.1)
|
cell.updateUI(accountID: item.0, source: item.1)
|
||||||
}
|
}
|
||||||
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
|
return UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
|
||||||
switch itemIdentifier {
|
switch itemIdentifier {
|
||||||
|
case .loadingIndicator:
|
||||||
|
return collectionView.dequeueConfiguredReusableCell(using: loadingCell, for: indexPath, item: ())
|
||||||
case .account(let id, let source):
|
case .account(let id, let source):
|
||||||
return collectionView.dequeueConfiguredReusableCell(using: accountCell, for: indexPath, item: (id, source))
|
return collectionView.dequeueConfiguredReusableCell(using: accountCell, for: indexPath, item: (id, source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let loadingView = UICollectionView.SupplementaryRegistration<LoadingCollectionViewCell>(elementKind: UICollectionView.elementKindSectionHeader) { view, elementKind, indexPath in
|
|
||||||
view.indicator.startAnimating()
|
|
||||||
}
|
|
||||||
dataSource.supplementaryViewProvider = { collectionView, elementKind, indexPath in
|
|
||||||
if elementKind == UICollectionView.elementKindSectionHeader {
|
|
||||||
return collectionView.dequeueConfiguredReusableSupplementary(using: loadingView, for: indexPath)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dataSource
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -91,10 +107,9 @@ class SuggestedProfilesViewController: UIViewController, CollectionViewControlle
|
||||||
}
|
}
|
||||||
state = .loading
|
state = .loading
|
||||||
|
|
||||||
layout.showSectionHeader = true
|
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
snapshot.appendSections([.accounts])
|
snapshot.appendSections([.loadingIndicator])
|
||||||
|
snapshot.appendItems([.loadingIndicator])
|
||||||
await dataSource.apply(snapshot)
|
await dataSource.apply(snapshot)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -103,8 +118,6 @@ class SuggestedProfilesViewController: UIViewController, CollectionViewControlle
|
||||||
|
|
||||||
await mastodonController.persistentContainer.addAll(accounts: suggestions.map(\.account))
|
await mastodonController.persistentContainer.addAll(accounts: suggestions.map(\.account))
|
||||||
|
|
||||||
layout.showSectionHeader = false
|
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
snapshot.appendSections([.accounts])
|
snapshot.appendSections([.accounts])
|
||||||
snapshot.appendItems(suggestions.map { .account($0.account.id, $0.source) })
|
snapshot.appendItems(suggestions.map { .account($0.account.id, $0.source) })
|
||||||
|
@ -118,7 +131,6 @@ class SuggestedProfilesViewController: UIViewController, CollectionViewControlle
|
||||||
await self?.loadInitial()
|
await self?.loadInitial()
|
||||||
}
|
}
|
||||||
showToast(configuration: config, animated: true)
|
showToast(configuration: config, animated: true)
|
||||||
layout.showSectionHeader = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,9 +146,11 @@ extension SuggestedProfilesViewController {
|
||||||
|
|
||||||
extension SuggestedProfilesViewController {
|
extension SuggestedProfilesViewController {
|
||||||
enum Section {
|
enum Section {
|
||||||
|
case loadingIndicator
|
||||||
case accounts
|
case accounts
|
||||||
}
|
}
|
||||||
enum Item: Hashable {
|
enum Item: Hashable {
|
||||||
|
case loadingIndicator
|
||||||
case account(String, Suggestion.Source)
|
case account(String, Suggestion.Source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,17 +50,9 @@ class TrendingLinksViewController: UIViewController, CollectionViewController {
|
||||||
|
|
||||||
case .links:
|
case .links:
|
||||||
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(280))
|
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(280))
|
||||||
let group: NSCollectionLayoutGroup
|
let item = NSCollectionLayoutItem(layoutSize: size)
|
||||||
if let maximumReadableWidth = environment.maximumReadableWidth,
|
let item2 = NSCollectionLayoutItem(layoutSize: size)
|
||||||
environment.container.contentSize.width >= maximumReadableWidth {
|
let group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitems: [item, item2])
|
||||||
let width = (environment.container.contentSize.width - 48) / 2
|
|
||||||
group = .horizontal(layoutSize: size, subitems: [
|
|
||||||
NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(width), heightDimension: .estimated(280))),
|
|
||||||
NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(width), heightDimension: .estimated(280))),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
group = .vertical(layoutSize: size, subitems: [NSCollectionLayoutItem(layoutSize: size)])
|
|
||||||
}
|
|
||||||
group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
|
group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
|
||||||
group.interItemSpacing = .fixed(16)
|
group.interItemSpacing = .fixed(16)
|
||||||
let section = NSCollectionLayoutSection(group: group)
|
let section = NSCollectionLayoutSection(group: group)
|
||||||
|
|
|
@ -62,7 +62,9 @@ class TrendingStatusesViewController: UIViewController, CollectionViewController
|
||||||
}
|
}
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
|
|
@ -68,7 +68,9 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
|
||||||
}
|
}
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
|
|
@ -117,8 +117,6 @@ class MainSplitViewController: UISplitViewController {
|
||||||
guard mode != navigationMode else {
|
guard mode != navigationMode else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
navigationMode = mode
|
|
||||||
|
|
||||||
let viewControllers = secondaryNavController.viewControllers
|
let viewControllers = secondaryNavController.viewControllers
|
||||||
secondaryNavController.viewControllers = []
|
secondaryNavController.viewControllers = []
|
||||||
// Setting viewControllers = [] doesn't remove the VC views from their superviews immediately,
|
// Setting viewControllers = [] doesn't remove the VC views from their superviews immediately,
|
||||||
|
|
|
@ -93,7 +93,9 @@ class NotificationsCollectionViewController: UIViewController, TimelineLikeColle
|
||||||
}
|
}
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
|
|
@ -91,7 +91,9 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
return .list(using: config, layoutEnvironment: environment)
|
return .list(using: config, layoutEnvironment: environment)
|
||||||
} else {
|
} else {
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,6 @@ class StatusActionAccountListCollectionViewController: UIViewController, Collect
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in
|
||||||
let section: NSCollectionLayoutSection
|
|
||||||
switch dataSource.sectionIdentifier(for: sectionIndex)! {
|
switch dataSource.sectionIdentifier(for: sectionIndex)! {
|
||||||
case .status:
|
case .status:
|
||||||
var config = UICollectionLayoutListConfiguration(appearance: .grouped)
|
var config = UICollectionLayoutListConfiguration(appearance: .grouped)
|
||||||
|
@ -72,12 +71,14 @@ class StatusActionAccountListCollectionViewController: UIViewController, Collect
|
||||||
config.trailingSwipeActionsConfigurationProvider = { [unowned self] in
|
config.trailingSwipeActionsConfigurationProvider = { [unowned self] in
|
||||||
(collectionView.cellForItem(at: $0) as? TimelineStatusCollectionViewCell)?.trailingSwipeActions()
|
(collectionView.cellForItem(at: $0) as? TimelineStatusCollectionViewCell)?.trailingSwipeActions()
|
||||||
}
|
}
|
||||||
section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
|
return section
|
||||||
case .accounts:
|
case .accounts:
|
||||||
section = NSCollectionLayoutSection.list(using: accountsConfig, layoutEnvironment: environment)
|
return NSCollectionLayoutSection.list(using: accountsConfig, layoutEnvironment: environment)
|
||||||
}
|
}
|
||||||
section.readableContentInset(in: environment)
|
|
||||||
return section
|
|
||||||
}
|
}
|
||||||
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
|
|
|
@ -54,7 +54,9 @@ class StatusEditHistoryViewController: UIViewController, CollectionViewControlle
|
||||||
}
|
}
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
|
|
@ -109,10 +109,12 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
// Use the sectionProvider closure, because the content inset depends on the environment.
|
// just setting layout.configuration.contentInsetsReference doesn't work with UICollectionViewCompositionalLayout.list
|
||||||
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
let layout = UICollectionViewCompositionalLayout { sectionIndex, environment in
|
||||||
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
let section = NSCollectionLayoutSection.list(using: config, layoutEnvironment: environment)
|
||||||
section.readableContentInset(in: environment)
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
section.contentInsetsReference = .readableContent
|
||||||
|
}
|
||||||
return section
|
return section
|
||||||
}
|
}
|
||||||
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
NS_INLINE NSException * _Nullable catchNSException(void(^_Nonnull tryBlock)(void)) {
|
NS_INLINE NSException * _Nullable catchNSException(void(^_Nonnull tryBlock)(void)) {
|
||||||
@try {
|
@try {
|
||||||
|
@ -14,8 +13,3 @@ NS_INLINE NSException * _Nullable catchNSException(void(^_Nonnull tryBlock)(void
|
||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define this private method so we can override it from MultiColumnCollectionViewLayout.
|
|
||||||
@interface UICollectionViewLayout (Tusker_Hacks)
|
|
||||||
-(UICollectionViewLayoutInvalidationContext *)_invalidationContextForUpdatedLayoutMargins:(UIEdgeInsets)newMargins;
|
|
||||||
@end
|
|
||||||
|
|
Loading…
Reference in New Issue