// // FocusedAttachmentController.swift // ComposeUI // // Created by Shadowfacts on 4/29/23. // import SwiftUI import MatchedGeometryPresentation import AVKit class FocusedAttachmentController: ViewController { unowned let parent: ComposeController let attachment: DraftAttachment let thumbnailController: AttachmentThumbnailController private let player: AVPlayer? init(parent: ComposeController, attachment: DraftAttachment, thumbnailController: AttachmentThumbnailController) { self.parent = parent self.attachment = attachment self.thumbnailController = thumbnailController if case let .file(url, type) = attachment.data, type.conforms(to: .movie) { self.player = AVPlayer(url: url) self.player!.isMuted = true } else { self.player = nil } } var view: some View { FocusedAttachmentView(attachment: attachment) } struct FocusedAttachmentView: View { @ObservedObject var attachment: DraftAttachment @EnvironmentObject private var controller: FocusedAttachmentController @Environment(\.dismiss) private var dismiss @FocusState private var textEditorFocused: Bool @EnvironmentObject private var matchedGeomState: MatchedGeometryState var body: some View { VStack(spacing: 0) { Spacer(minLength: 0) if let player = controller.player { VideoPlayer(player: player) .matchedGeometryDestination(id: attachment.id) .onAppear { player.play() } } else if #available(iOS 16.0, *) { ZoomableScrollView { attachmentView .matchedGeometryDestination(id: attachment.id) } } else { attachmentView .matchedGeometryDestination(id: attachment.id) } Spacer(minLength: 0) FocusedAttachmentDescriptionView(attachment: attachment) .environment(\.colorScheme, .dark) .matchedGeometryDestination(id: AttachmentDescriptionTextViewID(attachment)) .frame(height: 150) .focused($textEditorFocused) } .background(.black) .overlay(alignment: .topLeading, content: { Button { // set the mode to dismissing immediately, so that layout changes due to the keyboard hiding // (which happens before the dismiss animation controller starts running) don't alter the destination frames if textEditorFocused { matchedGeomState.mode = .dismissing } dismiss() } label: { Image(systemName: "arrow.down.forward.and.arrow.up.backward") } .buttonStyle(DismissFocusedAttachmentButtonStyle()) .padding([.top, .leading], 4) }) } private var attachmentView: some View { ControllerView(controller: { controller.thumbnailController }) .environment(\.attachmentThumbnailConfiguration, .init(contentMode: .fit, fullSize: true)) } } } private struct DismissFocusedAttachmentButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { ZStack { RoundedRectangle(cornerRadius: 4) .fill(.black.opacity(0.5)) configuration.label .foregroundColor(.white) .imageScale(.large) } .frame(width: 40, height: 40) } } struct AttachmentDescriptionTextViewID: Hashable { let attachmentID: UUID! init(_ attachment: DraftAttachment) { self.attachmentID = attachment.id } func hash(into hasher: inout Hasher) { hasher.combine(attachmentID) hasher.combine("descriptionTextView") } }