Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // BottomSheetVC.swift
- // BeFriends
- //
- // Created by Ramesh Sanghar on 17/04/23.
- //
- import UIKit
- class ShareBottomSheetVC: UIViewController {
- // 1
- lazy var containerView: UIView = {
- let view = UIView()
- view.backgroundColor = .white
- view.layer.cornerRadius = 16
- view.clipsToBounds = true
- return view
- }()
- // define lazy views
- lazy var titleLabel: UILabel = {
- let label = UILabel()
- label.textColor = .lightGray
- label.translatesAutoresizingMaskIntoConstraints = false
- label.text = "GET IN TOUCH"
- label.font = .systemFont(ofSize: 15, weight: .light)
- return label
- }()
- lazy var lineView: UIView = {
- let line = UIView()
- line.translatesAutoresizingMaskIntoConstraints = false
- line.backgroundColor = StaticValues.TINT_COLOR
- return line
- }()
- var shareView: [UIView] = []
- lazy var contentStackView: UIStackView = {
- let stackView = UIStackView()
- stackView.axis = .horizontal
- stackView.spacing = 20
- stackView.alignment = .center
- stackView.distribution = .equalSpacing
- return stackView
- }()
- // 2
- let maxDimmedAlpha: CGFloat = 0.6
- lazy var dimmedView: UIView = {
- let view = UIView()
- view.backgroundColor = .black
- view.alpha = maxDimmedAlpha
- return view
- }()
- let defaultHeight: CGFloat = 300
- let dismissibleHeight: CGFloat = 200
- let maximumContainerHeight: CGFloat = UIScreen.main.bounds.height - 64
- // keep updated with new height
- var currentContainerHeight: CGFloat = 300
- // 3. Dynamic container constraint
- var containerViewHeightConstraint: NSLayoutConstraint?
- var containerViewBottomConstraint: NSLayoutConstraint?
- var isLargeSizeEnabled = true
- let shareTitles = ["WhatsApp","Instagram","Write to us","Visit Us"]
- let shareImages = ["message","camera","envelope","globe"]
- override func viewDidLoad() {
- super.viewDidLoad()
- let imageSize: CGFloat = 30
- for i in 0...3 {
- let sView = UIView()
- sView.backgroundColor = .clear
- sView.translatesAutoresizingMaskIntoConstraints = false
- sView.heightAnchor.constraint(equalToConstant: 70).isActive = true
- sView.widthAnchor.constraint(equalToConstant: 70).isActive = true
- let imageView = UIImageView()
- imageView.translatesAutoresizingMaskIntoConstraints = false
- imageView.image = UIImage(systemName: shareImages[i])?.withAlignmentRectInsets(UIEdgeInsets(top: 5, left: -5, bottom: -5, right: 5))
- imageView.tintColor = .white
- imageView.contentMode = .scaleAspectFit
- imageView.backgroundColor = StaticValues.TINT_COLOR
- // imageView.image?.inse = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
- sView.addSubview(imageView)
- imageView.topAnchor.constraint(equalTo: sView.topAnchor, constant: 5).isActive = true
- imageView.centerXAnchor.constraint(equalTo: sView.centerXAnchor).isActive = true
- let nameLbl = UILabel()
- nameLbl.backgroundColor = .clear
- nameLbl.translatesAutoresizingMaskIntoConstraints = false
- nameLbl.text = shareTitles[i]
- nameLbl.textColor = StaticValues.TINT_COLOR
- nameLbl.textAlignment = .center
- nameLbl.font = UIFont.systemFont(ofSize: 12, weight: .medium)
- sView.addSubview(nameLbl)
- nameLbl.heightAnchor.constraint(equalToConstant: 24).isActive = true
- nameLbl.leadingAnchor.constraint(equalTo: sView.leadingAnchor).isActive = true
- nameLbl.trailingAnchor.constraint(equalTo: sView.trailingAnchor).isActive = true
- nameLbl.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 5).isActive = true
- imageView.cornerRadius = imageView.halfHeight
- contentStackView.addArrangedSubview(sView)
- shareView.append(sView)
- imageView.heightAnchor.constraint(equalToConstant: imageSize).isActive = true
- imageView.widthAnchor.constraint(equalToConstant: imageSize).isActive = true
- imageView.clipsToBounds = true
- imageView.layer.cornerRadius = imageSize/2
- imageView.layer.masksToBounds = true
- }
- setupView()
- setupConstraints()
- setupPanGesture()
- }
- override func viewDidAppear(_ animated: Bool) {
- super.viewDidAppear(animated)
- animateShowDimmedView()
- animatePresentContainer()
- }
- override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
- let touch = touches.first
- if touch?.view == dimmedView {
- animateDismissView()
- }
- }
- func setupView() {
- view.backgroundColor = .clear
- }
- @objc func handlePanGesture(gesture: UIPanGestureRecognizer) {
- let translation = gesture.translation(in: view)
- // Drag to top will be minus value and vice versa
- // print("Pan gesture y offset: \(translation.y)")
- // Get drag direction
- let isDraggingDown = translation.y > 0
- // print("Dragging direction: \(isDraggingDown ? "going down" : "going up")")
- // New height is based on value of dragging plus current container height
- let newHeight = currentContainerHeight - translation.y
- // Handle based on gesture state
- switch gesture.state {
- case .changed:
- // This state will occur when user is dragging
- if newHeight < maximumContainerHeight {
- // Keep updating the height constraint
- containerViewHeightConstraint?.constant = newHeight
- // refresh layout
- view.layoutIfNeeded()
- }
- case .ended:
- // This happens when user stop drag,
- // so we will get the last height of container
- // Condition 1: If new height is below min, dismiss controller
- if newHeight < dismissibleHeight {
- self.animateDismissView()
- }
- else {
- self.animateContainerHeight(defaultHeight)
- }
- default:
- break
- }
- }
- func animateContainerHeight(_ height: CGFloat) {
- UIView.animate(withDuration: 0.4) {
- // Update container height
- self.containerViewHeightConstraint?.constant = height
- // Call this to trigger refresh constraint
- self.view.layoutIfNeeded()
- }
- // Save current height
- currentContainerHeight = height
- }
- func setupPanGesture() {
- // add pan gesture recognizer to the view controller's view (the whole screen)
- let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture(gesture:)))
- // change to false to immediately listen on gesture movement
- panGesture.delaysTouchesBegan = false
- panGesture.delaysTouchesEnded = false
- view.addGestureRecognizer(panGesture)
- }
- func animateShowDimmedView() {
- dimmedView.alpha = 0
- UIView.animate(withDuration: 0.4) {
- self.dimmedView.alpha = self.maxDimmedAlpha
- }
- }
- func animatePresentContainer() {
- // Update bottom constraint in animation block
- UIView.animate(withDuration: 0.3) {
- self.containerViewBottomConstraint?.constant = 0
- // Call this to trigger refresh constraint
- self.view.layoutIfNeeded()
- }
- }
- func animateDismissView() {
- // hide main container view by updating bottom constraint in animation block
- UIView.animate(withDuration: 0.3) {
- self.containerViewBottomConstraint?.constant = self.defaultHeight
- // call this to trigger refresh constraint
- self.view.layoutIfNeeded()
- }
- // hide blur view
- dimmedView.alpha = maxDimmedAlpha
- UIView.animate(withDuration: 0.4) {
- self.dimmedView.alpha = 0
- } completion: { _ in
- // once done, dismiss without animation
- self.dismiss(animated: false)
- }
- }
- func setupConstraints() {
- // 4. Add subviews
- view.addSubview(dimmedView)
- view.addSubview(containerView)
- dimmedView.translatesAutoresizingMaskIntoConstraints = false
- containerView.translatesAutoresizingMaskIntoConstraints = false
- containerView.addSubview(titleLabel)
- containerView.addSubview(lineView)
- containerView.addSubview(contentStackView)
- contentStackView.translatesAutoresizingMaskIntoConstraints = false
- // 5. Set static constraints
- NSLayoutConstraint.activate([
- // set dimmedView edges to superview
- dimmedView.topAnchor.constraint(equalTo: view.topAnchor),
- dimmedView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
- dimmedView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- dimmedView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- // set container static constraint (trailing & leading)
- containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- //containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: defaultHeight)
- ])
- // 6. Set container to default height
- containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: defaultHeight)
- // 7. Set bottom constant to 0
- containerViewBottomConstraint = containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
- // Activate constraints
- containerViewHeightConstraint?.isActive = true
- containerViewBottomConstraint?.isActive = true
- NSLayoutConstraint.activate([
- // TitleLbl
- titleLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 12),
- titleLabel.heightAnchor.constraint(equalToConstant: 20),
- titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10),
- titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -10),
- ])
- NSLayoutConstraint.activate([
- // Line View
- lineView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5),
- lineView.heightAnchor.constraint(equalToConstant: 1),
- lineView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -10),
- lineView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10)
- ])
- NSLayoutConstraint.activate([
- // ..
- // content stackView
- contentStackView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 32),
- // contentStackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20),
- contentStackView.heightAnchor.constraint(equalToConstant: 150),
- contentStackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20),
- contentStackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20),
- ])
- }
- }
- let vc = ShareBottomSheetVC()
- vc.modalPresentationStyle = .overCurrentContext
- // Keep animated value as false
- // Custom Modal presentation animation will be handled in VC itself
- self.present(vc, animated: false)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement