import UIKit import Messages class GIFCollectionViewController: UIViewController { private var collectionView: UICollectionView! private let addButton = UIButton(type: .system) private let emptyStateLabel = UILabel() private let reuseIdentifier = "GIFCell" private var gifs: [GIF] = [] var onSelectGIF: ((GIF) -> Void)? override func viewDidLoad() { super.viewDidLoad() setupCollectionView() setupUI() setupGestureRecognizers() loadGIFs() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) loadGIFs() } private func setupCollectionView() { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.minimumLineSpacing = 10 layout.minimumInteritemSpacing = 10 // Calculate cell size to fit 2 cells per row with spacing let cellWidth = (view.bounds.width - 40) / 2 // 40 = padding (10 + 10) + spacing between cells (10) + extra margins (10) layout.itemSize = CGSize(width: cellWidth, height: cellWidth) layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .systemBackground collectionView.delegate = self collectionView.dataSource = self collectionView.register(GIFCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier) collectionView.alwaysBounceVertical = true if #available(iOS 14.0, *) { // Use collection view's built-in contextual menu support // This is set up in collectionView(_:contextMenuConfigurationForItemAt:point:) } } private func setupUI() { view.backgroundColor = .systemBackground // Add Collection View view.addSubview(collectionView) collectionView.translatesAutoresizingMaskIntoConstraints = false // Setup Add Button addButton.setImage(UIImage(systemName: "plus.circle.fill"), for: .normal) addButton.tintColor = .systemBlue addButton.contentHorizontalAlignment = .fill addButton.contentVerticalAlignment = .fill addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside) view.addSubview(addButton) addButton.translatesAutoresizingMaskIntoConstraints = false // Setup Empty State Label emptyStateLabel.text = "No GIFs saved yet. Add your first GIF!" emptyStateLabel.textAlignment = .center emptyStateLabel.textColor = .systemGray emptyStateLabel.numberOfLines = 0 view.addSubview(emptyStateLabel) emptyStateLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), addButton.widthAnchor.constraint(equalToConstant: 44), addButton.heightAnchor.constraint(equalToConstant: 44), addButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), addButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16), emptyStateLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), emptyStateLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor), emptyStateLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32), emptyStateLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32) ]) updateEmptyState() } func loadGIFs() { gifs = GIFStorageService.shared.fetchGIFs() collectionView.reloadData() updateEmptyState() } private func updateEmptyState() { emptyStateLabel.isHidden = !gifs.isEmpty } @objc private func addButtonTapped() { let addGIFVC = AddGIFViewController() addGIFVC.onSaveGIF = { [weak self] urlString, gifData in GIFStorageService.shared.saveGIF(data: gifData, fromURL: urlString) { _ in DispatchQueue.main.async { self?.loadGIFs() } } } addGIFVC.onCancel = { [weak self] in self?.dismiss(animated: true) } let navController = UINavigationController(rootViewController: addGIFVC) navController.modalPresentationStyle = .formSheet present(navController, animated: true) } private func setupGestureRecognizers() { // For iOS versions earlier than 14, we'll use a long press gesture recognizer if #unavailable(iOS 14.0) { let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:))) collectionView.addGestureRecognizer(longPressGesture) } } @objc private func handleLongPress(_ gesture: UILongPressGestureRecognizer) { if gesture.state == .began { let point = gesture.location(in: collectionView) guard let indexPath = collectionView.indexPathForItem(at: point) else { return } // Show action sheet for pre-iOS 14 devices showDeleteActionSheet(for: indexPath) } } private func showDeleteActionSheet(for indexPath: IndexPath) { let gif = gifs[indexPath.item] let alertController = UIAlertController( title: "GIF Options", message: "What would you like to do with this GIF?", preferredStyle: .actionSheet ) alertController.addAction(UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in self?.deleteGIF(at: indexPath) }) alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel)) present(alertController, animated: true) } private func deleteGIF(at indexPath: IndexPath) { let gif = gifs[indexPath.item] GIFStorageService.shared.deleteGIF(with: gif.id) // Remove from local array and update collection view gifs.remove(at: indexPath.item) collectionView.deleteItems(at: [indexPath]) updateEmptyState() } } extension GIFCollectionViewController: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return gifs.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? GIFCollectionViewCell else { return UICollectionViewCell() } let gif = gifs[indexPath.item] cell.configure(with: gif) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let gif = gifs[indexPath.item] onSelectGIF?(gif) } // MARK: - Context Menu Support (iOS 14+) @available(iOS 14.0, *) func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { let gif = gifs[indexPath.item] return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in let deleteAction = UIAction( title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive ) { [weak self] _ in self?.deleteGIF(at: indexPath) } return UIMenu(title: "", children: [deleteAction]) } } }