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() 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 } 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() } private 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] gif in GIFStorageService.shared.saveGIF(gif) self?.loadGIFs() } addGIFVC.onCancel = { [weak self] in self?.dismiss(animated: true) } let navController = UINavigationController(rootViewController: addGIFVC) navController.modalPresentationStyle = .formSheet present(navController, animated: true) } } 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.urlString) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let gif = gifs[indexPath.item] onSelectGIF?(gif) } }