Как сделать заголовок страницы в UICollectionView?

Для того чтобы добавить заголовок страницы в UICollectionView, нужно использовать класс UICollectionReusableView и протокол UICollectionViewDelegateFlowLayout.

Начнем с создания пользовательского заголовочного класса, который будет наследоваться от UICollectionReusableView. Для этого вы можете создать новый файл класса в вашем проекте, назовите его, например, "CustomHeaderCollectionReusableView".

import UIKit

class CustomHeaderCollectionReusableView: UICollectionReusableView {
    
    let titleLabel: UILabel = {
        let label = UILabel()
        // Настройки для заголовка, вы можете настроить их в соответствии с вашим дизайном
        label.font = UIFont.boldSystemFont(ofSize: 20)
        label.textAlignment = .center
        label.textColor = UIColor.black
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(titleLabel)
        
        // Настройка ограничений для заголовка
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true
        titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Затем в вашем контроллере коллекции, где вы используете UICollectionViewDataSource и UICollectionViewDelegateFlowLayout, вам потребуется реализовать метод collectionView(_:viewForSupplementaryElementOfKind:at:) из протокола UICollectionViewDelegateFlowLayout.

import UIKit

class YourCollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    // ...
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        // Проверяем, что это элемент заголовка
        if kind == UICollectionView.elementKindSectionHeader {
            
            // Создаем экземпляр пользовательского заголовочного класса
            let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "CustomHeader", for: indexPath) as! CustomHeaderCollectionReusableView
            
            // Устанавливаем заголовок
            headerView.titleLabel.text = "Заголовок"
            
            return headerView
        }
        
        return UICollectionReusableView()
    }
    
    // ...
}

Чтобы этот метод сработал на вашем контроллере коллекции, вам также нужно зарегистрировать класс вашего пользовательского заголовка в методе viewDidLoad вашего контроллера:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Регистрируем пользовательский заголовок
    collectionView.register(CustomHeaderCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "CustomHeader")
    
    // ...
}

Наконец, убедитесь, что ваш контроллер коллекции правильно настроен для работы с UICollectionViewDataSource и UICollectionViewDelegateFlowLayout, и что вы возвращаете правильное количество разделов и элементов в вашем методе numberOfSections(in:) и collectionView(_:numberOfItemsInSection:), соответственно.