0

So I found the following UI pattern online and I have been attempting to implement it in Xcode. However, I have been unsuccessful. I am unsure as to whether to create the optimal approach would be to

  1. create three different UIViewControllers (in which case I am not sure as to how to get them to animate in and out of view/how to get them to overlap one another)
  2. or to use a UITableView with custom overlapping cells. However, I am not sure whether this approach will allow me to animate properly upon pressing each cell. For this second approach, I saw this post, but this solution does not allow for touch interaction in the overlapping areas, something which I need.

I looked for libraries online that would allow for functionality such as this, but I was unsuccessful in finding any. The animation I am trying to achieve can be found here.

mlz7
  • 2,067
  • 3
  • 27
  • 51

1 Answers1

0

I would use a StackView to hold all the views of your ViewControllers. Then you can set the stack view's spacing property to a negative value to make the views overlap. If you wish show the complete view of one of the view controllers on tap, you add a tap gesture recognizer to that view that changes the stackView's spacing for just that view to 0.

A really simple example:

class PlaygroundViewController: UIViewController {

    let firstViewController = UIViewController()
    let secondViewController = UIViewController()
    let thirdViewController = UIViewController()

    lazy var viewControllersToAdd = [firstViewController, secondViewController, thirdViewController]

    let heightOfView: CGFloat = 300
    let viewOverlap: CGFloat = 200

    let stackView = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.white
        firstViewController.view.backgroundColor = .red
        secondViewController.view.backgroundColor = .blue
        thirdViewController.view.backgroundColor = .green

        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.spacing = -viewOverlap

        viewControllersToAdd.forEach { (controller: UIViewController) in
            if let childView = controller.view {

                stackView.addArrangedSubview(childView)
                NSLayoutConstraint.activate([
                    childView.heightAnchor.constraint(equalToConstant: heightOfView),
                    childView.widthAnchor.constraint(equalTo: stackView.widthAnchor)
                    ])

                let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapChildView(sender:)))
                childView.addGestureRecognizer(gestureRecognizer)
                childView.isUserInteractionEnabled = true
            }
            addChild(controller)
            controller.didMove(toParent: self)
        }

        view.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
            stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            ])
    }

    @objc func didTapChildView(sender: UITapGestureRecognizer) {

        if let targetView = sender.view {
            UIView.animate(withDuration: 0.3, animations: {
                let currentSpacing = self.stackView.customSpacing(after: targetView)
                if currentSpacing == 0 {
                    // targetView is already expanded, collapse it
                    self.stackView.setCustomSpacing(-self.viewOverlap, after: targetView)
                } else {
                    // expand view
                    self.stackView.setCustomSpacing(0, after: targetView)
                }
            })


        }
    }
}

flannerykj
  • 91
  • 6