diff --git a/Packages/ComposeUI/Sources/ComposeUI/ComposeInput.swift b/Packages/ComposeUI/Sources/ComposeUI/ComposeInput.swift index 375fabdb..5581eb7f 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/ComposeInput.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/ComposeInput.swift @@ -30,11 +30,12 @@ enum ToolbarElement { } private struct FocusedComposeInput: FocusedValueKey { - typealias Value = any ComposeInput + typealias Value = (any ComposeInput)? } extension FocusedValues { - var composeInput: (any ComposeInput)? { + // double optional is necessary pre-iOS 16 + var composeInput: (any ComposeInput)?? { get { self[FocusedComposeInput.self] } set { self[FocusedComposeInput.self] = newValue } } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Controllers/AttachmentRowController.swift b/Packages/ComposeUI/Sources/ComposeUI/Controllers/AttachmentRowController.swift index ff11ef04..5b56ad89 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Controllers/AttachmentRowController.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Controllers/AttachmentRowController.swift @@ -221,16 +221,3 @@ extension AttachmentRowController { case allowEntry, recognizingText } } - -private extension View { - @available(iOS, obsoleted: 16.0) - @available(visionOS 1.0, *) - @ViewBuilder - func contextMenu(@ViewBuilder menuItems: () -> M, @ViewBuilder previewIfAvailable preview: () -> P) -> some View { - if #available(iOS 16.0, *) { - self.contextMenu(menuItems: menuItems, preview: preview) - } else { - self.contextMenu(menuItems: menuItems) - } - } -} diff --git a/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift b/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift index 058c9aca..88cf7845 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift @@ -509,18 +509,6 @@ public final class ComposeController: ViewController { } } -private extension View { - @available(iOS, obsoleted: 16.0) - @ViewBuilder - func scrollDismissesKeyboardInteractivelyIfAvailable() -> some View { - if #available(iOS 16.0, *) { - self.scrollDismissesKeyboard(.interactively) - } else { - self - } - } -} - private struct GlobalFrameOutsideListPrefKey: PreferenceKey { static var defaultValue: CGRect = .zero static func reduce(value: inout CGRect, nextValue: () -> CGRect) { diff --git a/Packages/ComposeUI/Sources/ComposeUI/View+ForwardsCompat.swift b/Packages/ComposeUI/Sources/ComposeUI/View+ForwardsCompat.swift index 7b1dc3c3..4e7ab6e7 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/View+ForwardsCompat.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/View+ForwardsCompat.swift @@ -23,4 +23,33 @@ extension View { } } #endif + + @available(iOS, obsoleted: 16.0) + @ViewBuilder + func scrollDismissesKeyboardInteractivelyIfAvailable() -> some View { + #if os(visionOS) + self.scrollDismissesKeyboard(.interactively) + #else + if #available(iOS 16.0, *) { + self.scrollDismissesKeyboard(.interactively) + } else { + self + } + #endif + } + + @available(iOS, obsoleted: 16.0) + @available(visionOS 1.0, *) + @ViewBuilder + func contextMenu(@ViewBuilder menuItems: () -> M, @ViewBuilder previewIfAvailable preview: () -> P) -> some View { + #if os(visionOS) + self.contextMenu(menuItems: menuItems, preview: preview) + #else + if #available(iOS 16.0, *) { + self.contextMenu(menuItems: menuItems, preview: preview) + } else { + self.contextMenu(menuItems: menuItems) + } + #endif + } } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift index 7b6d967f..e9e8567e 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift @@ -45,7 +45,7 @@ struct AttachmentRowView: View { EditDrawingButton(attachment: attachment) RecognizeTextButton(attachment: attachment, isRecognizingText: $isRecognizingText, error: $textRecognitionError) DeleteButton(attachment: attachment) - } preview: { + } previewIfAvailable: { // TODO: need to fix flash of preview changing size AttachmentThumbnailView(attachment: attachment) } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentThumbnailView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentThumbnailView.swift index abe6c7c9..84e8d90d 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentThumbnailView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentThumbnailView.swift @@ -114,9 +114,21 @@ private struct AttachmentThumbnailViewContent: View { let asset = AVURLAsset(url: url) let imageGenerator = AVAssetImageGenerator(asset: asset) imageGenerator.appliesPreferredTrackTransform = true + #if os(visionOS) if let (cgImage, _) = try? await imageGenerator.image(at: .zero) { self.mode = .image(UIImage(cgImage: cgImage)) } + #else + if #available(iOS 16.0, *) { + if let (cgImage, _) = try? await imageGenerator.image(at: .zero) { + self.mode = .image(UIImage(cgImage: cgImage)) + } + } else { + if let cgImage = try? imageGenerator.copyCGImage(at: .zero, actualTime: nil) { + self.mode = .image(UIImage(cgImage: cgImage)) + } + } + #endif } enum Mode { diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentCollectionViewCell.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentCollectionViewCell.swift index 49991ce8..42edc810 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentCollectionViewCell.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentCollectionViewCell.swift @@ -9,12 +9,14 @@ import UIKit import SwiftUI final class AttachmentCollectionViewCell: UICollectionViewCell { - let attachmentView: UIView & UIContentView + let attachmentView: UIView// & UIContentView override init(frame: CGRect) { - attachmentView = UIHostingConfiguration(content: { - AttachmentCollectionViewCellView(attachment: nil) - }).makeContentView() + attachmentView = UIView() + attachmentView.backgroundColor = .red +// attachmentView = UIHostingConfiguration(content: { +// AttachmentCollectionViewCellView(attachment: nil) +// }).makeContentView() super.init(frame: frame) @@ -27,9 +29,9 @@ final class AttachmentCollectionViewCell: UICollectionViewCell { } func updateUI(attachment: DraftAttachment) { - attachmentView.configuration = UIHostingConfiguration(content: { - AttachmentCollectionViewCellView(attachment: attachment) - }).margins(.all, .zero) +// attachmentView.configuration = UIHostingConfiguration(content: { +// AttachmentCollectionViewCellView(attachment: attachment) +// }).margins(.all, .zero) } } @@ -127,6 +129,7 @@ private struct RoundedSquare: Shape { } } +@available(iOS 16.0, *) private struct SquareFrame: Layout { func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { precondition(subviews.count == 1) @@ -145,10 +148,21 @@ private struct SquareFrame: Layout { } private extension View { + @ViewBuilder func squareFrame() -> some View { + #if os(visionOS) SquareFrame { self } + #else + if #available(iOS 16.0, *) { + SquareFrame { + self + } + } else { + self + } + #endif } } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift index 8f8995e7..f3683a53 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift @@ -70,8 +70,8 @@ private class WrappedCollectionViewController: UIViewController { let layout = UICollectionViewCompositionalLayout { [unowned self] section, environment in let (itemSize, itemsPerRow) = self.itemSize(width: environment.container.contentSize.width) - let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(itemSize), heightDimension: .absolute(itemSize))) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(itemSize)), repeatingSubitem: item, count: itemsPerRow) + let items = Array(repeating: NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(itemSize), heightDimension: .absolute(itemSize))), count: itemsPerRow) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(itemSize)), subitems: items) group.interItemSpacing = .fixed(spacing) let section = NSCollectionLayoutSection(group: group) section.interGroupSpacing = spacing @@ -81,9 +81,10 @@ private class WrappedCollectionViewController: UIViewController { cell.updateUI(attachment: attachment) } let addButtonCell = UICollectionView.CellRegistration { [unowned self] cell, indexPath, item in - cell.contentConfiguration = UIHostingConfiguration(content: { - AddAttachmentButton(viewController: self, enabled: item) - }).margins(.all, .zero) + cell.contentView.backgroundColor = .blue +// cell.contentConfiguration = UIHostingConfiguration(content: { +// AddAttachmentButton(viewController: self, enabled: item) +// }).margins(.all, .zero) } let collectionView = IntrinsicContentSizeCollectionView(frame: .zero, collectionViewLayout: layout) self.view = collectionView diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeToolbarView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeToolbarView.swift index f5c20b04..e83e8b4e 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeToolbarView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeToolbarView.swift @@ -72,7 +72,7 @@ private struct ToolbarScrollView: View { } } } - .scrollDisabled(realWidth ?? 0 <= minWidth ?? 0) + .scrollDisabledIfAvailable(realWidth ?? 0 <= minWidth ?? 0) .frame(maxWidth: .infinity) .background { GeometryReader { proxy in @@ -182,7 +182,7 @@ private struct InsertEmojiButton: View { @ScaledMetric(relativeTo: .body) private var imageSize: CGFloat = 22 var body: some View { - if input?.toolbarElements.contains(.emojiPicker) == true { + if input??.toolbarElements.contains(.emojiPicker) == true { Button(action: beginAutocompletingEmoji) { Label("Insert custom emoji", systemImage: "face.smiling") } @@ -195,7 +195,7 @@ private struct InsertEmojiButton: View { } private func beginAutocompletingEmoji() { - input?.beginAutocompletingEmoji() + input??.beginAutocompletingEmoji() } } @@ -204,7 +204,7 @@ private struct FormatButtons: View { @PreferenceObserving(\.$statusContentType) private var contentType var body: some View { - if let input, + if let input = input.flatMap(\.self), input.toolbarElements.contains(.formattingButtons), contentType != .plain { diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift index 0d5c789f..e6cec01b 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift @@ -15,10 +15,28 @@ struct ComposeView: View { @EnvironmentObject private var controller: ComposeController var body: some View { + navigation + .environmentObject(mastodonController.instanceFeatures) + } + + @ViewBuilder + private var navigation: some View { + #if os(visionOS) NavigationStack { navigationRoot } - .environmentObject(mastodonController.instanceFeatures) + #else + if #available(iOS 16.0, *) { + NavigationStack { + navigationRoot + } + } else { + NavigationView { + navigationRoot + } + .navigationViewStyle(.stack) + } + #endif } private var navigationRoot: some View { @@ -26,7 +44,7 @@ struct ComposeView: View { ScrollView { scrollContent } - .scrollDismissesKeyboard(.interactively) + .scrollDismissesKeyboardInteractivelyIfAvailable() #if !os(visionOS) && !targetEnvironment(macCatalyst) .modifier(ToolbarSafeAreaInsetModifier()) #endif diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift index 1e5f7e3d..53d3674a 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift @@ -62,7 +62,8 @@ private struct LanguageButton: View { @State private var hasChanged = false var body: some View { - if instanceFeatures.createStatusWithLanguage { + if #available(iOS 16.0, *), + instanceFeatures.createStatusWithLanguage { LanguagePicker(draftLanguage: $draft.language, hasChangedSelection: $hasChanged) .buttonStyle(LanguageButtonStyle()) .onReceive(NotificationCenter.default.publisher(for: UITextInputMode.currentInputModeDidChangeNotification), perform: currentInputModeChanged) @@ -72,10 +73,11 @@ private struct LanguageButton: View { } } + @available(iOS 16.0, *) private func currentInputModeChanged(_ notification: Foundation.Notification) { guard !hasChanged, !draft.hasContent, - let mode = input?.textInputMode, + let mode = input??.textInputMode, let code = LanguagePicker.codeFromInputMode(mode) else { return } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/NewMainTextView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/NewMainTextView.swift index 17cb8df7..baaa56fe 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/NewMainTextView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/NewMainTextView.swift @@ -41,7 +41,11 @@ private struct NewMainTextViewRepresentable: UIViewRepresentable { func makeUIView(context: Context) -> UITextView { // TODO: if we're not doing the pill background, reevaluate whether this version fork is necessary - let view = WrappedTextView(usingTextLayoutManager: true) + let view = if #available(iOS 16.0, *) { + WrappedTextView(usingTextLayoutManager: true) + } else { + WrappedTextView() + } view.addInteraction(UIDropInteraction(delegate: context.coordinator)) view.delegate = context.coordinator view.adjustsFontForContentSizeCategory = true