Advertisement
srk72

Bottom Sheet small and large iOS

Apr 24th, 2023
1,583
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 10.74 KB | None | 0 0
  1. //
  2. //  FeedbackBottomSheetVC.swift
  3. //  BeFriends
  4. //
  5. //  Created by Ramesh Sanghar on 18/04/23.
  6. //
  7.  
  8. import UIKit
  9.  
  10. class FeedbackBottomSheetVC: UIViewController {
  11.  
  12.     // 1
  13.     lazy var containerView: UIView = {
  14.         let view = UIView()
  15.         view.backgroundColor = .white
  16.         view.layer.cornerRadius = 16
  17.         view.clipsToBounds = true
  18.         return view
  19.     }()
  20.    
  21.     // define lazy views
  22.     lazy var titleLabel: UILabel = {
  23.         let label = UILabel()
  24.         label.textColor = .lightGray
  25.         label.translatesAutoresizingMaskIntoConstraints = false
  26.         label.text = "FEEDBACK"
  27.         label.font = .systemFont(ofSize: 15, weight: .light)
  28.         return label
  29.     }()
  30.    
  31.     lazy var sendBtn: UIButton = {
  32.         let button = UIButton(type: .system)
  33.         button.setTitle("SEND", for: .normal)
  34.         button.setTitleColor(StaticValues.TINT_COLOR, for: .normal)
  35.         button.translatesAutoresizingMaskIntoConstraints = false
  36.         button.addTarget(self, action: #selector(sendAction), for: .touchUpInside)
  37.         return button
  38.     }()
  39.    
  40.     lazy var lineView: UIView = {
  41.         let line = UIView()
  42.         line.translatesAutoresizingMaskIntoConstraints = false
  43.         line.backgroundColor = StaticValues.TINT_COLOR
  44.         return line
  45.     }()
  46.    
  47.     // define lazy views
  48.     lazy var feedBackTextView: UITextView = {
  49.         let text = UITextView()
  50.         text.textColor = .lightGray
  51.         text.translatesAutoresizingMaskIntoConstraints = false
  52.         text.text = "Your Text Here..."
  53.         text.font = .systemFont(ofSize: 18, weight: .regular)
  54.         return text
  55.     }()
  56.    
  57.     // 2
  58.     let maxDimmedAlpha: CGFloat = 0.6
  59.     lazy var dimmedView: UIView = {
  60.         let view = UIView()
  61.         view.backgroundColor = .black
  62.         view.alpha = maxDimmedAlpha
  63.         return view
  64.     }()
  65.    
  66.     let defaultHeight: CGFloat = 300
  67.     let dismissibleHeight: CGFloat = 200
  68.     let maximumContainerHeight: CGFloat = UIScreen.main.bounds.height - 64
  69.     // keep updated with new height
  70.     var currentContainerHeight: CGFloat = 300
  71.    
  72.    
  73.     // 3. Dynamic container constraint
  74.     var containerViewHeightConstraint: NSLayoutConstraint?
  75.     var containerViewBottomConstraint: NSLayoutConstraint?
  76.    
  77.     var isLargeSizeEnabled = true
  78.    
  79.     override func viewDidLoad() {
  80.         super.viewDidLoad()
  81.         setupView()
  82.         setupConstraints()
  83.         setupPanGesture()
  84.     }
  85.    
  86.     override func viewDidAppear(_ animated: Bool) {
  87.         super.viewDidAppear(animated)
  88.         animateShowDimmedView()
  89.         animatePresentContainer()
  90.     }
  91.    
  92.     override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  93.        
  94.         let touch = touches.first
  95.        
  96.         if touch?.view == dimmedView {
  97.             animateDismissView()
  98.         }
  99.     }
  100.    
  101.     @objc private func sendAction() {
  102.        
  103.     }
  104.    
  105.     func setupView() {
  106.         view.backgroundColor = .clear
  107.     }
  108.    
  109.     @objc func handlePanGesture(gesture: UIPanGestureRecognizer) {
  110.         let translation = gesture.translation(in: view)
  111.         // Drag to top will be minus value and vice versa
  112.       //  print("Pan gesture y offset: \(translation.y)")
  113.  
  114.         // Get drag direction
  115.         let isDraggingDown = translation.y > 0
  116.       //  print("Dragging direction: \(isDraggingDown ? "going down" : "going up")")
  117.  
  118.         // New height is based on value of dragging plus current container height
  119.         let newHeight = currentContainerHeight - translation.y
  120.  
  121.         // Handle based on gesture state
  122.         switch gesture.state {
  123.         case .changed:
  124.             // This state will occur when user is dragging
  125.             if newHeight < maximumContainerHeight {
  126.                 // Keep updating the height constraint
  127.                 containerViewHeightConstraint?.constant = newHeight
  128.                 // refresh layout
  129.                 view.layoutIfNeeded()
  130.             }
  131.         case .ended:
  132.             // This happens when user stop drag,
  133.             // so we will get the last height of container
  134.             // Condition 1: If new height is below min, dismiss controller
  135.             if newHeight < dismissibleHeight {
  136.                 self.animateDismissView()
  137.             }
  138.             else if newHeight < defaultHeight {
  139.                 // Condition 2: If new height is below default, animate back to default
  140.                 animateContainerHeight(defaultHeight)
  141.             }
  142.             else if newHeight < maximumContainerHeight && isDraggingDown {
  143.                 // Condition 3: If new height is below max and going down, set to default height
  144.                 animateContainerHeight(defaultHeight)
  145.             }
  146.             else if newHeight > defaultHeight && !isDraggingDown {
  147.                 // Condition 4: If new height is below max and going up, set to max height at top
  148.                 animateContainerHeight(maximumContainerHeight)
  149.             }
  150.         default:
  151.             break
  152.         }
  153.     }
  154.    
  155.     func animateContainerHeight(_ height: CGFloat) {
  156.         UIView.animate(withDuration: 0.4) {
  157.             // Update container height
  158.             self.containerViewHeightConstraint?.constant = height
  159.             // Call this to trigger refresh constraint
  160.             self.view.layoutIfNeeded()
  161.         }
  162.         // Save current height
  163.         currentContainerHeight = height
  164.     }
  165.    
  166.     func setupPanGesture() {
  167.         // add pan gesture recognizer to the view controller's view (the whole screen)
  168.         let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture(gesture:)))
  169.         // change to false to immediately listen on gesture movement
  170.         panGesture.delaysTouchesBegan = false
  171.         panGesture.delaysTouchesEnded = false
  172.         view.addGestureRecognizer(panGesture)
  173.     }
  174.    
  175.     func animateShowDimmedView() {
  176.         dimmedView.alpha = 0
  177.         UIView.animate(withDuration: 0.4) {
  178.             self.dimmedView.alpha = self.maxDimmedAlpha
  179.         }
  180.     }
  181.    
  182.     func animatePresentContainer() {
  183.         // Update bottom constraint in animation block
  184.         UIView.animate(withDuration: 0.3) {
  185.             self.containerViewBottomConstraint?.constant = 0
  186.             // Call this to trigger refresh constraint
  187.             self.view.layoutIfNeeded()
  188.         }
  189.     }
  190.    
  191.     func animateDismissView() {
  192.         // hide main container view by updating bottom constraint in animation block
  193.         UIView.animate(withDuration: 0.3) {
  194.             self.containerViewBottomConstraint?.constant = self.defaultHeight
  195.             // call this to trigger refresh constraint
  196.             self.view.layoutIfNeeded()
  197.         }
  198.        
  199.         // hide blur view
  200.         dimmedView.alpha = maxDimmedAlpha
  201.         UIView.animate(withDuration: 0.4) {
  202.             self.dimmedView.alpha = 0
  203.         } completion: { _ in
  204.             // once done, dismiss without animation
  205.             self.dismiss(animated: false)
  206.         }
  207.     }
  208.    
  209.     func setupConstraints() {
  210.         // 4. Add subviews
  211.         view.addSubview(dimmedView)
  212.         view.addSubview(containerView)
  213.         dimmedView.translatesAutoresizingMaskIntoConstraints = false
  214.         containerView.translatesAutoresizingMaskIntoConstraints = false
  215.        
  216.         containerView.addSubview(titleLabel)
  217.         containerView.addSubview(sendBtn)
  218.         containerView.addSubview(lineView)
  219.         containerView.addSubview(feedBackTextView)
  220.        
  221.         // 5. Set static constraints
  222.         NSLayoutConstraint.activate([
  223.             // set dimmedView edges to superview
  224.             dimmedView.topAnchor.constraint(equalTo: view.topAnchor),
  225.             dimmedView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
  226.             dimmedView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  227.             dimmedView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  228.             // set container static constraint (trailing & leading)
  229.             containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
  230.             containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
  231.             //containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: defaultHeight)
  232.         ])
  233.        
  234.         // 6. Set container to default height
  235.         containerViewHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: defaultHeight)
  236.         // 7. Set bottom constant to 0
  237.         containerViewBottomConstraint = containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
  238.         // Activate constraints
  239.         containerViewHeightConstraint?.isActive = true
  240.         containerViewBottomConstraint?.isActive = true
  241.        
  242.         NSLayoutConstraint.activate([
  243.             // TitleLbl
  244.             titleLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 12),
  245.             titleLabel.heightAnchor.constraint(equalToConstant: 20),
  246.             titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10),
  247.             titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -10),
  248.         ])
  249.        
  250.         NSLayoutConstraint.activate([
  251.             // Send Btn
  252.             sendBtn.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),
  253.             sendBtn.widthAnchor.constraint(equalToConstant: 80),
  254.             sendBtn.heightAnchor.constraint(equalToConstant: 30),
  255.             sendBtn.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -5),
  256.         ])
  257.        
  258.         NSLayoutConstraint.activate([
  259.             // Line View
  260.             lineView.topAnchor.constraint(equalTo: sendBtn.bottomAnchor, constant: 5),
  261.             lineView.heightAnchor.constraint(equalToConstant: 1),
  262.             lineView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -10),
  263.             lineView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10)
  264.         ])
  265.        
  266.         NSLayoutConstraint.activate([
  267.             // Feedback Text View
  268.             feedBackTextView.topAnchor.constraint(equalTo: lineView.bottomAnchor, constant: 18),
  269.             feedBackTextView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -30),
  270.             feedBackTextView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -10),
  271.             feedBackTextView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10)
  272.         ])
  273.        
  274.     }
  275. }
  276.  
  277.  
  278.  
  279. let vc = ShareBottomSheetVC()
  280.         vc.modalPresentationStyle = .overCurrentContext
  281.         // Keep animated value as false
  282.         // Custom Modal presentation animation will be handled in VC itself
  283.         self.present(vc, animated: false)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement