ارفع راسك فوق انته سوري حر ... :green_heart::green_heart:

Skip to content
Snippets Groups Projects
Select Git revision
  • ui-ux-improvments
  • signature
  • main default protected
3 results

DocumentsTableViewController.swift

Blame
  • DocumentsTableViewController.swift 21.33 KiB
    import UIKit
    import VisionKit
    import ImagePicker
    
    final class DocumentsTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
        
        struct Constants {
            static let reuseIdentifier = String(describing: DocumentsTableViewController.self)
            static let storyboardName = "Documents"
            static let rowHeight: CGFloat = 112
            static let cellReuseIdentifier = String(describing: DocumentsTableViewCell.self)
            
            static let topBottom: CGFloat = 120
            static let margin: CGFloat = 64
        }
        
        static func buildViewController() -> DocumentsTableViewController {
            let controller = UIStoryboard(name: Constants.storyboardName,
                                          bundle: .main).instantiateViewController(withIdentifier: Constants.reuseIdentifier)
            return controller as! DocumentsTableViewController
        }
        
        @IBOutlet weak var tableView: UITableView!
        @IBOutlet weak var AllFolderView: AllFolderTableViewCell!
        @IBOutlet weak var fixedTableSheet: UIView!
        @IBOutlet weak var searchForFilesView: SearchFilesView!
        
        private var viewModels: [File] = []
        private var searchedViewModel: [File] = []
        
        private var localFileManager: LocalFileManager?
        private var sortType: SortyFileType = .date
        
        //    private let noDocumentsimageView = UIImageView(image: UIImage(named: "box"))
        private var renameAlertController: UIAlertController?
        private var renameFileName: String?
        private var selectedFolder: AppConfigurator.Folder?
        private var isSearching: Bool = false
        private var pageViewControllers: [UIViewController] = []
        
        @IBOutlet weak var allFolderView_height: NSLayoutConstraint!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            navigationItem.title = "File Manager"
            let settingsButton = UIBarButtonItem(image: UIImage(named: "settings"), style: .done, target: self, action: #selector(openSettings))
            navigationItem.rightBarButtonItem = settingsButton
            
            tableView.register(UINib(nibName: "DocumentsTableViewCell", bundle: nil), forCellReuseIdentifier: "DocumentsTableViewCell")
            
            tableView.tableFooterView = UIView()
            tableView.delegate = self
            tableView.dataSource = self
            
            tableView.reloadData()
            
            let savedFolders = AppConfigurator().getFolders()
            for item in savedFolders {
                if item.isSelected == true {
                    selectedFolder = item
                }
            }
            
            if let folder = selectedFolder {
                AllFolderView?.set(selectedFolder: folder, delegate: self)
            }
            searchForFilesView.delegate = self
            localFileManager = LocalFileManager()
            
            tabBarController?.delegate = UIApplication.shared.delegate as? UITabBarControllerDelegate
            
            NotificationCenter.default.addObserver(self, selector: #selector(middleButtonTapped(_:)),
                                                   name: NSNotification.Name.MiddleButtonTapped, object: nil)
            
            NotificationCenter.default.addObserver(self, selector: #selector(fetchViewModels),
                                                   name: NSNotification.Name.ReloadViewModels, object: nil)
            
            NotificationCenter.default.addObserver(self, selector: #selector(rightButtonTapped(_:)),
                                                   name: NSNotification.Name.rightButtonTapped, object: nil)
            if UserDefaults.standard.startsAppWithCamera {
                openCamera()
            }
            
            fixedTableSheet.layer.cornerRadius = 30
            fixedTableSheet.layer.shadowColor = UIColor.gray.cgColor
            fixedTableSheet.layer.shadowOffset = CGSize(width: 0, height: 2) // Shadow position
            fixedTableSheet.layer.shadowOpacity = 0.7 // Shadow opacity
            fixedTableSheet.layer.shadowRadius = 4.0
            fixedTableSheet.backgroundColor = .white // or any non-clear color
            fixedTableSheet.clipsToBounds = false
            fetchViewModels()
            
        }
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
        }
        
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
        
        @objc private func fetchViewModels() {
            if let folder = selectedFolder {
                guard let directoryURL = localFileManager?.getFolderUrl(folder: folder),
                      let viewModels = localFileManager?.filesForDirectory(directoryURL, sortType: sortType) else { return }
                self.viewModels = viewModels
                self.searchedViewModel = viewModels
                AllFolderView?.set(selectedFolder: folder, delegate: self)
                tableView.reloadData()
            }
        }
        
        @objc private func openSettings() {
            let settings = SettingViewController()
            self.navigationController?.pushViewController(settings, animated: false)
        }
        
        // MARK: - Table view data source
        
        func numberOfSections(in tableView: UITableView) -> Int {
            // #warning Incomplete implementation, return the number of sections
            return 1
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            // #warning Incomplete implementation, return the number of rows
            return isSearching ? searchedViewModel.count :viewModels.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: Constants.cellReuseIdentifier,
                                                           for: indexPath) as? DocumentsTableViewCell else { return UITableViewCell() }
            
            // Configure the cell...
            let viewModel = isSearching ? searchedViewModel[indexPath.row ] : viewModels[indexPath.row]
            cell.configure(with: viewModel, image: localFileManager?.getThumbnail(for: viewModel.fileURL))
            
            return cell
            
        }
        
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return Constants.rowHeight
        }
        
        func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
            return UIView()
        }
        
        func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
            20
        }
        
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            let viewModel = isSearching ? searchedViewModel[indexPath.row] : viewModels[indexPath.row]
            let controller = DocumentPreviewViewController.buildViewController()
            controller.file = viewModel
            controller.hidesBottomBarWhenPushed = true
            navigationController?.pushViewController(controller, animated: true)
        }
        
        func tableView(_ tableView: UITableView,
                       editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
            return .none
        }
        
        func tableView(_ tableView: UITableView,
                       trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
            
            // Trash action
            let trash = UIContextualAction(style: .destructive,
                                           title: "Delete".localized) { [weak self] (_, _, completionHandler) in
                self?.deleteFile(at: indexPath)
                completionHandler(true)
            }
            trash.backgroundColor = .systemRed
            trash.image = UIImage(systemName: "trash")?.tint(with: .white)
            
            // Rename action
            let rename = UIContextualAction(style: .normal,
                                            title: "Rename".localized) { [weak self] (_, _, completionHandler) in
                self?.renameFile(at: indexPath)
                completionHandler(true)
            }
            rename.backgroundColor = UIColor.black.withAlphaComponent(0.5)
            rename.image = UIImage(systemName: "square.and.pencil")?.tint(with: .white)
            
            let configuration = UISwipeActionsConfiguration(actions: [trash, rename])
            configuration.performsFirstActionWithFullSwipe = true
            
            return configuration
        }
        
        @objc private func middleButtonTapped(_ notification: NSNotification) {
            openCamera()
        }
        
        @objc private func rightButtonTapped(_ notification: NSNotification) {
            openGallery()
        }
        
        
        private func openCamera() {
            let scannerOptions = ImageScannerOptions(scanMultipleItems: true,
                                                     allowAutoScan: false,
                                                     allowTapToFocus: false,
                                                     defaultColorRenderOption:.color)
            let scannerViewController = ScannerViewController(scanSession: nil, options: scannerOptions)
            scannerViewController.delegate = self
            
            scannerViewController.hidesBottomBarWhenPushed = true
            self.navigationController?.pushViewController(scannerViewController, animated: false)
        }
        
        private func openGallery() {
            
            let imagePickerController = ImagePickerController()
            imagePickerController.expandGalleryView()
            imagePickerController.delegate = self
            imagePickerController.modalPresentationStyle = .fullScreen
            present(imagePickerController, animated: true, completion: nil)
        }
        
        private func renameFile(at indexPath: IndexPath) {
            let viewModel = isSearching ? searchedViewModel[indexPath.row] : viewModels[indexPath.row]
            renameAlertController = UIAlertController(title: "Rename document".localized, message: nil, preferredStyle: .alert)
            
            renameAlertController!.addTextField { [weak self] textField in
                guard let self = self else { return }
                textField.placeholder = viewModel.displayName
                textField.text = viewModel.displayName
                textField.clearButtonMode = .always
                self.renameFileName = viewModel.displayName
                textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged)
            }
            
            let continueAction = UIAlertAction(title: "Rename".localized,
                                               style: .default) { [weak self] _ in
                guard let self = self else { return }
                guard let textFields = self.renameAlertController?.textFields else { return }
                
                if let newName = textFields.first?.text, newName != viewModel.displayName {
                    do {
                        try self.localFileManager?.renameFile(at: viewModel.fileURL, withName: newName)
                        self.fetchViewModels()
                    } catch {
                        return
                    }
                }
            }
            
            renameAlertController!.addAction(continueAction)
            renameAlertController!.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
            renameAlertController?.actions.first?.isEnabled = false
            present(renameAlertController!, animated: true)
        }
        
        private func deleteFile(at indexPath: IndexPath) {
            let viewModel = isSearching ? searchedViewModel[indexPath.row] : viewModels[indexPath.row]
            
            if UserDefaults.standard.askOnSwipeDelete {
                let alertController = UIAlertController(title: Bundle.appName,
                                                        message: "Are you sure you want to delete this document?".localized, preferredStyle: .alert)
                
                let okAction = UIAlertAction(title: "Yes, delete!".localized, style: .destructive, handler: { [weak self] action in
                    guard let self = self else { return }
                    
                    self.delete(file: viewModel.fileURL, at: indexPath)
                })
                
                alertController.addAction(okAction)
                
                alertController.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil))
                alertController.popoverPresentationController?.sourceView = tableView.cellForRow(at: indexPath)
                present(alertController, animated: true)
            } else {
                delete(file: viewModel.fileURL, at: indexPath)
            }
        }
        
        private func delete(file fileURL: URL, at indexPath: IndexPath) {
            do {
                try self.localFileManager?.filesManager.removeItem(at: fileURL)
                try self.localFileManager?.removeThumbnail(for: fileURL)
                if isSearching {
                    self.searchedViewModel.remove(at: indexPath.row)
                } else {
                    self.viewModels.remove(at: indexPath.row)
                }
                self.tableView.deleteRows(at: [indexPath], with: .fade)
            } catch {
                return
            }
        }
        
        
    }
    
    /// Tells the delegate that the user picked a still image.
    extension DocumentsTableViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        func imagePickerController(_ picker: UIImagePickerController,
                                   didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            picker.dismiss(animated: true) { [weak self] in
                guard let self = self else { return }
                guard let image = info[UserDefaults.standard.isPhotoEditigOn ? UIImagePickerController.InfoKey.editedImage : UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
                if let folder = selectedFolder {
                    PDFManager.createPDFDocument(from: image, localFileManager: self.localFileManager, folder: folder)
                    self.fetchViewModels()
                    self.tabBarController?.selectedIndex = 0
                }
            }
        }
        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            picker.dismiss(animated: true) { [weak self] in
                self?.tabBarController?.selectedIndex = 0
            }
        }
    }
    
    extension DocumentsTableViewController: ImageScannerControllerDelegate {
        func imageScannerController(_ scanner: ImageScannerController, didFinishWithSession session: MultiPageScanSession) {
            var images = [URL]()
            
            for index in 0..<session.scannedItems.count {
                if let url = session.scannedItems[index].renderedImage {
                    images.append(url)
                }
            }
            print("images: \(images)")
            if let folder = selectedFolder {
                PDFManager.createMultiPDFPage(from: images, localFileManager: localFileManager, folder: folder) {
                    scanner.dismiss(animated: true) {
                        self.fetchViewModels()
                    }
                }
            }
        }
        
        func imageScannerControllerDidCancel(_ scanner: ImageScannerController) {
            scanner.dismiss(animated: true)
        }
        
        func imageScannerController(_ scanner: ImageScannerController, didFailWithError error: Error) {
            scanner.dismiss(animated: true)
            
        }
        
        @objc func textFieldDidChange(_ textField: UITextField) {
            guard let text = textField.text, let renameFileName = renameFileName,
                  renameFileName != text, !text.trimmingCharacters(in: .whitespaces).isEmpty else {
                renameAlertController?.actions.first?.isEnabled = false
                return
            }
            
            renameAlertController?.actions.first?.isEnabled = true
        }
    }
    
    extension DocumentsTableViewController: ScannerViewControllerDelegate {
        func scannerViewController(_ scannerViewController: ScannerViewController, reviewItems inSession: MultiPageScanSession) {
            let multipageScanViewController = MultiPageScanSessionViewController(scanSession: inSession)
            multipageScanViewController.delegate = self
            self.navigationController?.pushViewController(multipageScanViewController, animated: true)
            
        }
        
        func scannerViewController(_ scannerViewController: ScannerViewController, didFail withError: Error) {
            scannerViewController.dismiss(animated: true)
        }
        
        func scannerViewControllerDidCancel(_ scannerViewController: ScannerViewController) {
            scannerViewController.dismiss(animated: true)
        }
    }
    
    extension DocumentsTableViewController: AllFolderTableViewCellDelegate {
        func addFolderTapped() {
            alert(confirm: "Add Folder", destructive: "Cancel", message: "Add Folder?"){ folderName in
                let newFolder = AppConfigurator.Folder(name: folderName, savedName: folderName.replacingOccurrences(of: " ", with: "_"), isSelected: false)
                var currentFolders = AppConfigurator().getFolders()
                currentFolders.append(newFolder)
                if let encoded = try? JSONEncoder().encode(currentFolders) {
                    UserDefaults.standard.set(encoded, forKey: "folders")
                    self.AllFolderView?.refresh()
                    self.dismiss(animated: true)
                }
            }
        }
        
        func cellTapped(folder: AppConfigurator.Folder) {
            selectedFolder = folder
            fetchViewModels()
        }
    }
    
    extension DocumentsTableViewController: SearchFilesViewDelegate {
        func searchfor(text: String) {
            if text.count == 0 {
                isSearching = false
                UIView.animate(withDuration: 0.5) {
                    self.allFolderView_height.constant = 240
                    self.AllFolderView.isHidden = false
                }
            } else {
                isSearching = true
                self.searchedViewModel =  self.viewModels.filter { $0.displayName.lowercased().contains(text.lowercased()) }
            }
            tableView.reloadData()
        }
    }
    
    extension DocumentsTableViewController: ImagePickerDelegate {
        
        func wrapperDidPress(_ imagePicker: ImagePicker.ImagePickerController, images: [UIImage]) {
            guard !images.isEmpty else { return }
            
            // Create a view controller for each image
            let viewControllers = images.map { image -> UIViewController in
                let viewController = UIViewController()
                viewController.view.backgroundColor = .white
                
                let imageView = UIImageView(image: image)
                imageView.contentMode = .scaleAspectFit
                imageView.translatesAutoresizingMaskIntoConstraints = false
                
                viewController.view.addSubview(imageView)
                NSLayoutConstraint.activate([
                    imageView.leadingAnchor.constraint(equalTo: viewController.view.leadingAnchor),
                    imageView.trailingAnchor.constraint(equalTo: viewController.view.trailingAnchor),
                    imageView.topAnchor.constraint(equalTo: viewController.view.topAnchor),
                    imageView.bottomAnchor.constraint(equalTo: viewController.view.bottomAnchor)
                ])
                
                return viewController
            }
            
            self.pageViewControllers = viewControllers
            // Create a page view controller to allow the user to scroll through the images
            let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
            pageViewController.dataSource = self
            
            pageViewController.setViewControllers([viewControllers[0]], direction: .forward, animated: false, completion: nil)
            
            // Present the page view controller
            imagePicker.present(pageViewController, animated: true, completion: nil)
        }
        
        
        func doneButtonDidPress(_ imagePicker: ImagePicker.ImagePickerController, images: [UIImage]) {
            imagePicker.dismiss(animated: true) { [weak self] in
                guard let self = self else { return }
                if let folder = selectedFolder {
                    PDFManager.createMultiImagesPDFDocument(from: images, localFileManager: self.localFileManager, folder: folder)
                    self.fetchViewModels()
                    self.tabBarController?.selectedIndex = 0
                }
            }
        }
        
        func cancelButtonDidPress(_ imagePicker: ImagePicker.ImagePickerController) {
            imagePicker.dismiss(animated: true) { [weak self] in
                self?.tabBarController?.selectedIndex = 0
            }
        }
    }
    extension DocumentsTableViewController: MultiPageScanSessionViewControllerDelegate {
        
        public func multiPageScanSessionViewController(_ multiPageScanSessionViewController: MultiPageScanSessionViewController,
                                                       finished session: MultiPageScanSession) {
            var images = [URL]()
            for index in 0..<session.scannedItems.count {
                if let url = session.scannedItems[index].renderedImage {
                    images.append(url)
                }
            }
            print("images: \(images)")
            if let folder = selectedFolder {
                PDFManager.createMultiPDFPage(from: images, localFileManager: localFileManager, folder: folder) {
                    self.fetchViewModels()
                    self.navigationController?.popViewController(animated: true)
                }
            }
        }
    }
    
    extension DocumentsTableViewController: UIPageViewControllerDataSource {
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
            guard let index = pageViewControllers.firstIndex(of: viewController), index > 0 else {
                return nil
            }
            return pageViewControllers[index - 1]
        }
        
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
            guard let index = pageViewControllers.firstIndex(of: viewController), index < pageViewControllers.count - 1 else {
                return nil
            }
            return pageViewControllers[index + 1]
        }
    }