import AVFoundation import Photos import SwiftUI struct AttachmentMediaPickerView: View { @State private var isCameraAccessGranted = AVCaptureDevice.authorizationStatus(for: .video) == .authorized @State private var isGalleryAccessGranted = PHPhotoLibrary.authorizationStatus() == .authorized @State private var images = [UIImage]() let gridSize = UIScreen.main.bounds.width / 3 var body: some View { let columns = Array(repeating: GridItem(.flexible(), spacing: 0), count: 3) ScrollView(showsIndicators: false) { LazyVGrid(columns: columns, spacing: 0) { // For camera if isCameraAccessGranted { ZStack { CameraView() .aspectRatio(1, contentMode: .fit) .frame(maxWidth: .infinity) Image(systemName: "camera") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 40, height: 40) .foregroundColor(.white) .padding(8) .background(Color.black.opacity(0.5)) .clipShape(Circle()) .padding(8) } } else { Button { openAppSettings() } label: { ZStack { Rectangle() .fill(Color.Main.backgroundLight) .overlay { VStack { Image(systemName: "camera") .foregroundColor(.Material.tortoiseLight300) .font(.system(size: 30)) Text("Allow camera access") .foregroundColor(.Main.black) .font(.body3) } } .frame(height: 100) } } } // For pictures if isGalleryAccessGranted { ForEach(images.indices, id: \.self) { index in Image(uiImage: images[index]) .resizable() .aspectRatio(1, contentMode: .fit) .frame(maxWidth: .infinity) .clipped() } } else { Button { openAppSettings() } label: { ZStack { Rectangle() .fill(Color.Main.backgroundLight) .overlay { VStack { Image(systemName: "photo") .foregroundColor(.Material.tortoiseLight300) .font(.system(size: 30)) Text("Allow gallery access") .foregroundColor(.Main.black) .font(.body3) } } .frame(height: 100) } } } } } .onAppear { checkCameraAccess() checkGalleryAccess() } } private func checkCameraAccess() { let status = AVCaptureDevice.authorizationStatus(for: .video) switch status { case .authorized: isCameraAccessGranted = true case .notDetermined: AVCaptureDevice.requestAccess(for: .video) { granted in DispatchQueue.main.async { self.isCameraAccessGranted = granted } } case .denied, .restricted: isCameraAccessGranted = false @unknown default: isCameraAccessGranted = false } } private func checkGalleryAccess() { let status = PHPhotoLibrary.authorizationStatus() switch status { case .authorized, .limited: isGalleryAccessGranted = true fetchImages() case .notDetermined: PHPhotoLibrary.requestAuthorization { status in DispatchQueue.main.async { self.isGalleryAccessGranted = status == .authorized if self.isGalleryAccessGranted { self.fetchImages() } } } case .denied, .restricted: isGalleryAccessGranted = false @unknown default: isGalleryAccessGranted = false } } private func fetchImages() { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions) let manager = PHImageManager.default() let option = PHImageRequestOptions() option.isSynchronous = true assets.enumerateObjects { asset, _, _ in manager.requestImage( for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: option ) { image, _ in if let image = image { DispatchQueue.main.async { if let img = scaleAndCropImage(image, toSize: CGSize(width: gridSize, height: gridSize)) { self.images.append(img) } } } } } } func openAppSettings() { if let appSettingsUrl = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(appSettingsUrl) { UIApplication.shared.open(appSettingsUrl, completionHandler: nil) } } func scaleAndCropImage(_ image: UIImage, toSize size: CGSize) -> UIImage? { let imageView = UIImageView(frame: CGRect(origin: .zero, size: size)) imageView.contentMode = .scaleAspectFill imageView.image = image UIGraphicsBeginImageContextWithOptions(size, false, 0.0) guard let context = UIGraphicsGetCurrentContext() else { return nil } imageView.layer.render(in: context) let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result } } class CameraUIView: UIView { var previewLayer: AVCaptureVideoPreviewLayer? override func layoutSubviews() { super.layoutSubviews() previewLayer?.frame = bounds } } struct CameraView: UIViewRepresentable { func makeUIView(context _: Context) -> CameraUIView { let view = CameraUIView() let captureSession = AVCaptureSession() guard let captureDevice = AVCaptureDevice.default(for: .video) else { return view } guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return view } captureSession.addInput(input) let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.videoGravity = .resizeAspectFill view.layer.addSublayer(previewLayer) view.previewLayer = previewLayer captureSession.startRunning() return view } func updateUIView(_ uiView: CameraUIView, context _: Context) { // Update the previewLayer frame when the view updates uiView.previewLayer?.frame = uiView.bounds } } // struct AttachmentMediaPickerView: View { // @StateObject private var mediaManager = MediaManager() // // var body: some View { // ScrollView { // LazyVGrid(columns: Array(repeating: .init(.flexible()), count: 3)) { // ForEach(elements) { element in // element // } // } // .padding(.horizontal, 8) // } // .padding(.vertical, 8) // } // // private var elements: [GridElement] { // print("Creating elements") // var result: [GridElement] = [] // // // camera // if let feed = mediaManager.cameraFeed, mediaManager.cameraAccessLevel == .authorized { // result.append(GridElement(id: UUID(), type: .cameraFeed, content: feed) { // print("Go to capture???") // }) // print("Added camera feed") // } else if mediaManager.cameraAccessLevel == .restricted { // result.append(GridElement(id: UUID(), type: .cameraRestricted, content: nil) { // print("Show alert") // }) // print("Added camera restricted") // } else { // result.append(GridElement(id: UUID(), type: .cameraAskButton, content: nil) { // mediaManager.openAppSettings() // }) // print("Added camera ask button") // } // // // photos // // if mediaManager.galleryAccessLevel == .restricted { // // result.append(GridElement(id: UUID(), type: .photoRestricted, content: nil)) // // } else { // // for photo in mediaManager.photos { // // result.append(GridElement(id: UUID(), type: .photo, content: photo)) // // } // // if mediaManager.galleryAccessLevel != .authorized { // // result.append(GridElement(id: UUID(), type: .photoAskButton, content: nil)) // // } // // } // // return result // } // } // // private enum GridElementType { // case cameraFeed // case cameraAskButton // case cameraRestricted // case photo // case photoAskButton // case photoRestricted // } // // private struct GridElement: View, Identifiable { // let id: UUID // let type: GridElementType // let content: UIImage? // let action: () -> Void // // var body: some View { // switch type { // case .cameraFeed: // image // .resizable() // .aspectRatio(contentMode: .fill) // .frame(width: 100, height: 100) // .clipped() // // case .cameraAskButton: // Button { // action() // } label: { // RoundedRectangle(cornerRadius: 5) // .stroke(Color.Main.backgroundDark, lineWidth: 2) // .overlay { // Image(systemName: "camera") // .foregroundColor(.Material.tortoiseLight300) // .font(.system(size: 40)) // } // .frame(height: 100) // // .resizable() // // .aspectRatio(contentMode: .fill) // // .frame(width: 100, height: 100) // // .clipped() // } // // case .photo: // image // .resizable() // .aspectRatio(contentMode: .fill) // .frame(width: 100, height: 100) // .clipped() // // case .photoAskButton: // Button { // action() // } label: { // Image(systemName: "photo.badge.plus") // .resizable() // .aspectRatio(contentMode: .fill) // .frame(width: 100, height: 100) // .clipped() // } // // case .photoRestricted, .cameraRestricted: // Button { // action() // } label: { // Image(systemName: "cross") // .resizable() // .aspectRatio(contentMode: .fill) // .frame(width: 100, height: 100) // .clipped() // } // } // } // // private var image: Image { // guard let content = content else { // return Image(systemName: "questionmark.square.dashed") // } // return Image(uiImage: content) // } // } // // struct AttachmentMediaPickerView_Previews: PreviewProvider { // static var previews: some View { // AttachmentMediaPickerView() // } // }