import UIKit class AddGIFViewController: UIViewController { private let titleLabel: UILabel = { let label = UILabel() label.text = "Add New GIF" label.font = .systemFont(ofSize: 18, weight: .bold) label.textAlignment = .center return label }() private let urlTextField: UITextField = { let textField = UITextField() textField.placeholder = "Enter GIF URL" textField.borderStyle = .roundedRect textField.autocorrectionType = .no textField.autocapitalizationType = .none textField.keyboardType = .URL textField.returnKeyType = .next textField.clearButtonMode = .whileEditing return textField }() private let previewGIFPlayer: GIFPlayerView = { let player = GIFPlayerView() player.clipsToBounds = true player.layer.cornerRadius = 8 player.backgroundColor = .systemGray6 return player }() private let loadingIndicator: UIActivityIndicatorView = { let indicator = UIActivityIndicatorView(style: .medium) indicator.hidesWhenStopped = true return indicator }() private let saveButton: UIButton = { let button = UIButton(type: .system) button.setTitle("Save", for: .normal) button.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) button.backgroundColor = .systemBlue button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 8 button.isEnabled = false return button }() private let cancelButton: UIButton = { let button = UIButton(type: .system) button.setTitle("Cancel", for: .normal) button.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) return button }() private var currentTask: URLSessionDataTask? var onSaveGIF: ((GIF) -> Void)? var onCancel: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() setupUI() setupActions() } private func setupUI() { view.backgroundColor = .systemBackground view.addSubview(titleLabel) view.addSubview(urlTextField) view.addSubview(previewGIFPlayer) view.addSubview(loadingIndicator) view.addSubview(saveButton) view.addSubview(cancelButton) titleLabel.translatesAutoresizingMaskIntoConstraints = false urlTextField.translatesAutoresizingMaskIntoConstraints = false previewGIFPlayer.translatesAutoresizingMaskIntoConstraints = false loadingIndicator.translatesAutoresizingMaskIntoConstraints = false saveButton.translatesAutoresizingMaskIntoConstraints = false cancelButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16), titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), urlTextField.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 16), urlTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), urlTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), previewGIFPlayer.topAnchor.constraint(equalTo: urlTextField.bottomAnchor, constant: 16), previewGIFPlayer.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), previewGIFPlayer.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), previewGIFPlayer.heightAnchor.constraint(equalToConstant: 150), loadingIndicator.centerXAnchor.constraint(equalTo: previewGIFPlayer.centerXAnchor), loadingIndicator.centerYAnchor.constraint(equalTo: previewGIFPlayer.centerYAnchor), saveButton.topAnchor.constraint(equalTo: previewGIFPlayer.bottomAnchor, constant: 24), saveButton.leadingAnchor.constraint(equalTo: view.centerXAnchor, constant: 8), saveButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), saveButton.heightAnchor.constraint(equalToConstant: 44), cancelButton.topAnchor.constraint(equalTo: previewGIFPlayer.bottomAnchor, constant: 24), cancelButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), cancelButton.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant: -8), cancelButton.heightAnchor.constraint(equalToConstant: 44), ]) } private func setupActions() { urlTextField.delegate = self urlTextField.addTarget(self, action: #selector(urlTextDidChange), for: .editingChanged) saveButton.addTarget(self, action: #selector(saveButtonTapped), for: .touchUpInside) cancelButton.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside) } @objc private func urlTextDidChange() { guard let urlString = urlTextField.text, !urlString.isEmpty else { previewGIFPlayer.stopAnimating() saveButton.isEnabled = false return } loadGIFPreview(from: urlString) } private func loadGIFPreview(from urlString: String) { // Cancel any existing task currentTask?.cancel() guard let url = URL(string: urlString) else { saveButton.isEnabled = false return } loadingIndicator.startAnimating() previewGIFPlayer.stopAnimating() currentTask = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in DispatchQueue.main.async { guard let self = self else { return } self.loadingIndicator.stopAnimating() if let data = data, error == nil { self.previewGIFPlayer.loadGIF(from: data) self.previewGIFPlayer.startAnimating() self.saveButton.isEnabled = true } else { self.previewGIFPlayer.stopAnimating() self.saveButton.isEnabled = false } } } currentTask?.resume() } @objc private func saveButtonTapped() { guard let urlString = urlTextField.text, !urlString.isEmpty else { return } let gif = GIF(urlString: urlString) onSaveGIF?(gif) dismiss(animated: true) } @objc private func cancelButtonTapped() { onCancel?() dismiss(animated: true) } } extension AddGIFViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } }