본문 바로가기
iOS/예제

[iOS 예제] UIPanGestureRecognizer로 BottomSheet 만들어보기

by Sky Titan 2021. 5. 1.
728x90

UI 구성

  • 스크롤할 bottomSheet의 이름을 innerView라고 지음
  • innerView안에 touchArea인 headerView를 넣는다. (맨 위에 marker가 있는 round한 영역)

 

코드

//
//  ViewController.swift
//  Practice
//
//
import UIKit

class ViewController: BaseViewController {
    
    @IBOutlet weak var headerView: UIView!
    @IBOutlet weak var innerView: UIView!
    @IBOutlet weak var redView: UIView!
    @IBOutlet weak var greenView: UIView!
    @IBOutlet weak var blueView: UIView!
    
    var topLimit: CGFloat = 0 //상단 한계
    var bottomLimit: CGFloat = UIScreen.main.bounds.size.height//하단 한계
    var points: [CGFloat] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        
        calculatePoints()
        innerView.layer.cornerRadius = 15
        headerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(panned(sender:))))
    }
    
    override func viewDidAppear(_ animated: Bool) {
        innerView.frame.origin.y = points[0]
    }
    
    @objc func panned(sender : UIPanGestureRecognizer) {
        let location = sender.location(in: self.view)
        
        if sender.state != .ended {
            moveInnerView(locationY: location.y)
        } else {
            //touch 종료할 때 가장 가까운 지점 (point)로 이동하기
            moveToNearPoint(locationY: location.y)
        }
    }
  
    func moveInnerView(locationY: CGFloat) {
        let min = max(topLimit, min(bottomLimit, locationY))
        innerView.frame.origin.y = min
    }
    
    func calculatePoints() {
        points.append(bottomLimit - headerView.frame.size.height)
        points.append(points[0] - redView.frame.size.height)
        points.append(points[1] - greenView.frame.size.height)
        points.append(points[2] - blueView.frame.size.height)
        topLimit = points[3]
        bottomLimit = points[0]
    }
    
    func moveToNearPoint(locationY: CGFloat) {
        var min: CGFloat = 100000
        var min_point: CGFloat = 0
        
        //지점(point)들 중 현재 headerView의 위치와 가장 가까운 지점 찾기
        points.forEach { point in
            if min > abs(locationY - point) {
                min = abs(locationY - point)
                min_point = point
            }
        }
        
        moveInnerView(locationY: min_point)
    }
}
  • touchArea인 headerView에 UIPanGestureRecognizer를 이용하여 스크롤할 때마다의 location을 받아와서 innerView 전체를 이동시키는 원리.
  • touch가 종료될 때 (손가락을 화면에서 뗄 때) 미리 지정해놓은 지점들 중 가장 가까운 곳으로 이동한다.

 

 

 

 

728x90

댓글