// // SheetContainerViewController.swift // SheetImagePicker // // Created by Shadowfacts on 9/23/19. // Copyright © 2019 Shadowfacts. All rights reserved. // import UIKit public class SheetContainerViewController: UIViewController { let content: UIViewController public var detents: [Detent] = [.bottom, .middle, .top] { didSet { } } var topConstraint: NSLayoutConstraint! lazy var initialConstant: CGFloat = view.bounds.height / 2 public init(content: UIViewController) { self.content = content super.init(nibName: nil, bundle: nil) } required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override public func viewDidLoad() { super.viewDidLoad() addChild(content) content.didMove(toParent: self) view.addSubview(content.view) topConstraint = content.view.topAnchor.constraint(equalTo: view.topAnchor, constant: initialConstant) NSLayoutConstraint.activate([ content.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), content.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), topConstraint, content.view.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognized(_:))) content.view.addGestureRecognizer(panGesture) } @objc func panGestureRecognized(_ recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: initialConstant = topConstraint.constant case .changed: let translation = recognizer.translation(in: content.view) var realOffset = initialConstant + translation.y if realOffset < view.safeAreaInsets.top { print(realOffset) realOffset = view.safeAreaInsets.top - realOffset / pow(CGFloat(M_E), realOffset / view.safeAreaInsets.top) } topConstraint.constant = realOffset case .ended: if let offset = nearestDetentOffset(offset: topConstraint.constant) { let distance = abs(topConstraint.constant - offset) self.topConstraint.constant = offset let velocity = recognizer.velocity(in: view) let springVelocity = velocity.y / distance UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: springVelocity, animations: { self.view.layoutIfNeeded() }) } default: return } } func nearestDetentOffset(offset: CGFloat) -> CGFloat? { return detents.map { $0.offset(in: view) }.min { (a, b) -> Bool in return abs(offset - a) < abs(offset - b) } } }