rechsteiner / Parchment

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

viewWillDispear is called in unexpected situations #728

Open renshi-asada opened 3 months ago

renshi-asada commented 3 months ago

Since this commit, which was introduced in ver 4.0.0, viewWillDisappear is being called in unexpected situations.

Here is how to reproduce it:

  1. Place PagingViewController and a UILabel in ViewController using a UIStackView.
  2. Transition from FirstViewController in PagingViewController to SecondViewController using pushViewController.
  3. Go back and change the layout in ViewController (e.g., hiding the UILabel).

At this point, didLayoutSubviews is initialized in PagingViewController.viewDidDisappear, but since viewDidLayoutSubviews is not called during the screen transition, didLayoutSubviews remains in the initialized state. This causes viewWillDisappear to be called unexpectedly when layout changes occur.

Transitioning from the ViewController in PagingViewController and returning to it is a common pattern in many use cases, and it is essential to address this issue because screen layout changes are often assumed in such cases.

Please find the code snippet below that reproduces the issue:

import UIKit
import Parchment

class ViewController: UIViewController, FirstViewControllerDelegate {
    private let label: UILabel = {
        let label = UILabel()
        label.text = "Layout Changed!"
        label.textAlignment = .center
        label.isHidden = true
        return label
    }()

    private let stackView: UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.distribution = .fill
        stackView.alignment = .fill
        stackView.spacing = 10
        return stackView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        let firstViewController = FirstViewController()
        let secondViewController = SecondViewController()
        firstViewController.delegate = self

        let pagingViewController = PagingViewController(viewControllers: [
            firstViewController
        ])

        addChild(pagingViewController)
        stackView.addArrangedSubview(pagingViewController.view)
        pagingViewController.didMove(toParent: self)

        pagingViewController.view.translatesAutoresizingMaskIntoConstraints = false

        stackView.addArrangedSubview(label)

        view.addSubview(stackView)
        stackView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            stackView.topAnchor.constraint(equalTo: view.topAnchor)
        ])
    }

    func didTapChangeLayoutButton() {
        label.isHidden.toggle()
        view.setNeedsLayout()
    }
}

protocol FirstViewControllerDelegate: AnyObject {
    func didTapChangeLayoutButton()
}

class FirstViewController: UIViewController {

    weak var delegate: FirstViewControllerDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupUI()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("FirstViewController.viewWillDisappear")
    }

    func setupUI() {
        let firstButton = UIButton(type: .system)
        firstButton.setTitle("Navigate", for: .normal)
        firstButton.addTarget(self, action: #selector(navigateToSecondVC), for: .touchUpInside)
        firstButton.frame = CGRect(x: 50, y: 100, width: 100, height: 50)
        view.addSubview(firstButton)

        let secondButton = UIButton(type: .system)
        secondButton.setTitle("Change Layout", for: .normal)
        secondButton.addTarget(self, action: #selector(changeLayout), for: .touchUpInside)
        secondButton.frame = CGRect(x: 50, y: 200, width: 150, height: 50)
        view.addSubview(secondButton)
    }

    @objc func navigateToSecondVC() {
        let secondVC = SecondViewController()
        navigationController?.pushViewController(secondVC, animated: true)
    }

    @objc func changeLayout() {
        delegate?.didTapChangeLayoutButton()
    }
}

class SecondViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .blue
    }
}