Skip to content

Instantly share code, notes, and snippets.

@Edudjr
Last active February 5, 2021 11:42
Show Gist options
  • Save Edudjr/970adca5e47d10375f69765826e6b0c6 to your computer and use it in GitHub Desktop.
Save Edudjr/970adca5e47d10375f69765826e6b0c6 to your computer and use it in GitHub Desktop.
BottomSheet with variable size fitting collapsed view
//: A UIKit based Playground for presenting user interface
// based on: https://stackoverflow.com/questions/37967555/how-can-i-mimic-the-bottom-sheet-from-the-maps-app
// This example includes an autoresizing collapsed-view.
// Clicking on "testing" will add another label, updating the collapsed-view's height.
// Click and drag the red section (BottomSheet) to update its position.
import UIKit
import PlaygroundSupport
let screenHeight: CGFloat = 400
let screenWidth: CGFloat = 200
class BottomSheet: UIViewController {
let topPadding: CGFloat = 50
var bottomPadding: CGFloat {
screenHeight - collapsedView!.frame.height
}
private var collapsedView: UIView?
public var collapsedViewHeight: CGFloat {
collapsedView!.frame.height
}
public func addCollapsedView(_ innerView: UIView) {
collapsedView = innerView
innerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(innerView)
innerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
innerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
innerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
innerView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor).isActive = true
adjustCollapsedViewInitialHeight()
}
private func adjustCollapsedViewInitialHeight() {
view.layoutIfNeeded()
let bottomPadding = screenHeight - collapsedViewHeight
let bottomSheetHeight = view.frame.height
let bottomSheetWidth = view.frame.width
UIView.animate(withDuration: 0.2, delay: 0.0, options: [.allowUserInteraction], animations: {
self.view.frame = CGRect(x: 0,
y: bottomPadding,
width: bottomSheetWidth,
height: bottomSheetHeight)
})
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
adjustCollapsedViewInitialHeight()
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
let gesture = UIPanGestureRecognizer(target: self,
action: #selector(Self.panGesture))
view.addGestureRecognizer(gesture)
}
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translation(in: self.view)
let velocity = recognizer.velocity(in: self.view)
let y = self.view.frame.minY
if ( y + translation.y >= topPadding) && (y + translation.y <= bottomPadding ) {
self.view.frame = CGRect(x: 0, y: y + translation.y, width: view.frame.width, height: view.frame.height)
recognizer.setTranslation(CGPoint.zero, in: self.view)
}
if recognizer.state == .ended {
var duration = velocity.y < 0 ? Double((y - topPadding) / -velocity.y) : Double((bottomPadding - y) / velocity.y )
duration = duration > 1.3 ? 1 : duration
UIView.animate(withDuration: duration, delay: 0.0, options: [.allowUserInteraction], animations: {
if velocity.y >= 0 {
self.view.frame = CGRect(x: 0, y: self.bottomPadding, width: self.view.frame.width, height: self.view.frame.height)
} else {
self.view.frame = CGRect(x: 0, y: self.topPadding, width: self.view.frame.width, height: self.view.frame.height)
}
}, completion: nil)
}
}
}
class ViewController: UIViewController {
var stack = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .gray
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addBottomSheetView()
}
func addBottomSheetView() {
// 1- Init bottomSheetVC
let bottomSheetVC = BottomSheet()
bottomSheetVC.addCollapsedView(makeStackView())
// 2- Add bottomSheetVC as a child view
self.addChild(bottomSheetVC)
self.view.addSubview(bottomSheetVC.view)
bottomSheetVC.didMove(toParent: self)
}
func makeStackView() -> UIStackView {
stack.backgroundColor = .red
stack.axis = .vertical
stack.addArrangedSubview(makeLabel())
stack.addArrangedSubview(makeLabel())
return stack
}
func makeLabel() -> UILabel {
let label = UILabel()
label.text = "testing"
label.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(target: self, action: #selector(componentTapped))
label.addGestureRecognizer(gesture)
return label
}
@objc func componentTapped() {
stack.addArrangedSubview(makeLabel())
}
}
let vc = ViewController()
vc.view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = vc
@Edudjr
Copy link
Author

Edudjr commented Feb 5, 2021

Example:
expanding_bottomsheet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment