rechsteiner / Parchment

A paging view with a highly customizable menu ✨
MIT License
3.39k stars 420 forks source link

How to use custom header paging view? #632

Closed 168-7cm closed 1 year ago

168-7cm commented 2 years ago

I want use my own custom header view which change height by content. (variable height)

I wrote code below but something went long.

here is my movie.

https://user-images.githubusercontent.com/71382628/150822800-9af6d6ed-0d3b-4314-bc16-0ac3844decad.MP4

  1. HeaderPagingView
class HeaderPagingView: PagingView {

    var headerHeightConstraint: NSLayoutConstraint?

    private lazy var headerView: UIView = {
        let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 200))
        view.backgroundColor = .red
        return view
    }()

    override func setupConstraints() {
        addSubview(headerView)

        pageView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        headerView.translatesAutoresizingMaskIntoConstraints = false

        headerHeightConstraint = headerView.heightAnchor.constraint(equalToConstant: headerView.frame.height)
        headerHeightConstraint?.isActive = true

        NSLayoutConstraint.activate([
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor),
            collectionView.heightAnchor.constraint(equalToConstant: options.menuHeight),
            collectionView.topAnchor.constraint(equalTo: headerView.bottomAnchor),

            headerView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
            headerView.leadingAnchor.constraint(equalTo: leadingAnchor),
            headerView.trailingAnchor.constraint(equalTo: trailingAnchor),

            pageView.leadingAnchor.constraint(equalTo: leadingAnchor),
            pageView.trailingAnchor.constraint(equalTo: trailingAnchor),
            pageView.bottomAnchor.constraint(equalTo: bottomAnchor),
            pageView.topAnchor.constraint(equalTo: topAnchor),
        ])
    }
}
  1. HeaderPagingViewController
    class HeaderPagingViewController: PagingViewController {
    override func loadView() {
        view = HeaderPagingView(options: options, collectionView: collectionView, pageView: pageViewController.view)
    }
    }
  2. HeaderViewController

    
    class HeaderViewController: UIViewController {
    
    private let viewControllers = [
        UserPostsViewController.configure(),
        UserPostsViewController.configure(),
        UserPostsViewController.configure()
    ]
    
    private let pagingViewController = HeaderPagingViewController()
    
    private var headerConstraint: NSLayoutConstraint {
        let pagingView = pagingViewController.view as! HeaderPagingView
        return pagingView.headerHeightConstraint!
    }
    
    private var headerHeight: CGFloat {
        let pagingView = pagingViewController.view as! HeaderPagingView
        return pagingView.headerHeightConstraint!.constant
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        addChild(pagingViewController)
        view.addSubview(pagingViewController.view)
        pagingViewController.didMove(toParent: self)
    
        pagingViewController.selectedTextColor = .black
        pagingViewController.indicatorColor = .black
        pagingViewController.indicatorOptions = .visible(height: 1, zIndex: Int.max, spacing: .zero, insets: .zero)
    
        pagingViewController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            pagingViewController.view.topAnchor.constraint(equalTo: view.topAnchor),
            pagingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            pagingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            pagingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        ])
    
        pagingViewController.dataSource = self
        pagingViewController.delegate = self
        viewControllers.first?.collectionView.delegate = self
    }
    }

extension HeaderViewController: PagingViewControllerDataSource {

func pagingViewController(_: PagingViewController, viewControllerAt index: Int) -> UIViewController {
    let viewController = viewControllers[index]
    viewController.title = "View \(index)"
    let height = pagingViewController.options.menuHeight + headerHeight
    let insets = UIEdgeInsets(top: height, left: 0, bottom: 0, right: 0)
    viewController.collectionView.contentInset = insets
    viewController.collectionView.scrollIndicatorInsets = insets
    return viewController
}

func pagingViewController(_: PagingViewController, pagingItemAt index: Int) -> PagingItem {
    return PagingIndexItem(index: index, title: "View \(index)")
}

func numberOfViewControllers(in _: PagingViewController) -> Int {
    return viewControllers.count
}

}

extension HeaderViewController: PagingViewControllerDelegate {

func pagingViewController(_: PagingViewController, didScrollToItem _: PagingItem, startingViewController: UIViewController?, destinationViewController: UIViewController, transitionSuccessful: Bool) {
    guard let startingViewController = startingViewController as? UserPostsViewController else { return }
    guard let destinationViewController = destinationViewController as? UserPostsViewController else { return }
    if transitionSuccessful {
        startingViewController.collectionView.delegate = nil
        destinationViewController.collectionView.delegate = self
    }
}

func pagingViewController(_: PagingViewController, willScrollToItem _: PagingItem, startingViewController _: UIViewController, destinationViewController: UIViewController) {
    guard let destinationViewController = destinationViewController as? UserPostsViewController else { return }
    if let scrollView = destinationViewController.collectionView {
        let offset = headerConstraint.constant + pagingViewController.options.menuHeight
        scrollView.contentOffset = CGPoint(x: 0, y: -offset)
        updateScrollIndicatorInsets(in: scrollView)
    }
}

}

extension HeaderViewController: UICollectionViewDelegate {

func updateScrollIndicatorInsets(in scrollView: UIScrollView) {
    let offset = min(0, scrollView.contentOffset.y) * -1
    let insetTop = max(pagingViewController.options.menuHeight, offset)
    let insets = UIEdgeInsets(top: insetTop, left: 0, bottom: 0, right: 0)
    scrollView.scrollIndicatorInsets = insets
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    guard scrollView.contentOffset.y < 0 else {
        if headerConstraint.constant > 0 {
            headerConstraint.constant = 0
        }
        return
    }

    updateScrollIndicatorInsets(in: scrollView)
    let height = max(0, abs(scrollView.contentOffset.y) - pagingViewController.options.menuHeight)
    headerConstraint.constant = height
}

}

rechsteiner commented 1 year ago

Hi @168-7cm! Sorry for the late response. I hope you managed to find a solution for this, if not, you can check out the examples project to see how I would solve this.