관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] PanModal을 이용한 BottomSheet 사용 방법 본문

iOS 응용 (swift)

[iOS - swift] PanModal을 이용한 BottomSheet 사용 방법

jake-kim 2023. 4. 16. 23:23

* FloatingPanel을 이용하여 BottomSheet 사용 방법은 이전 포스팅 글 참고

PanModal 

  • PanModal
  • star의 갯수가 3.4k이고 사용하는쪽에서 쉽게 접근이 가능한 프레임워크

  • (현재 글 작성 기준으로) 최신 업데이트가 3년전이지만 쉽게 사용이 가능하고 직관적이어서 바텀시트 관련 처리를 할때 유용한 프레임워크

최신 업데이트가 3년전 - https://github.com/slackhq/PanModal

PanModal 사용 방법

  • cocoapods으로 설치
pod 'PanModal'
  • 바텀시트로 띄울 VC 구현
    • UILabel하나와 UITableView하나가 있는 평범한 UIViewController 준비
import UIKit

final class MyVC: UIViewController {
    private let label: UILabel = {
        let label = UILabel()
        label.text = "[jake iOS 앱 개발 알아가기 - pan modal 예제]"
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    private let tableView: UITableView = {
        let view = UITableView()
        view.allowsSelection = false
        view.backgroundColor = .clear
        view.separatorStyle = .none
        view.contentInset = .zero
        view.estimatedRowHeight = 34
        view.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    var scrollView: UIScrollView {
        tableView
    }
    
    private let items = (0...30).map(String.init)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        tableView.dataSource = self
        
        view.addSubview(label)
        view.addSubview(tableView)
        
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            label.topAnchor.constraint(equalTo: view.topAnchor),
        ])
        
        NSLayoutConstraint.activate([
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            tableView.topAnchor.constraint(equalTo: label.bottomAnchor),
        ])
    }
}

extension MyVC: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        items.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
        cell?.textLabel?.text = items[indexPath.row]
        return cell ?? UITableViewCell()
    }
}
  • 사용하려는 UIViewController에 PanModalPresentable 프로토콜을 준수하고 이 VC를 presentPanModal()로 띄우면 완성
    • PanModalPresentable을 준수하면 필수로 구현해야하는 (구현 안하면 컴파일에러가 나는) panScrollable: UIScrollView?가 있고 nil을 리턴하면 스크롤이 없는 바텀시트로 동작
import UIKit
import PanModal

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemBlue
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
            print("start")
            let vc = MyVC()
            vc.modalPresentationStyle = .fullScreen
            self.presentPanModal(vc)
        })
    }
}

extension MyVC: PanModalPresentable {
    var panScrollable: UIScrollView? {
        scrollView
    }
    
    var shortFormHeight: PanModalHeight {
        .contentHeight(UIScreen.main.bounds.height * 0.4)
    }
}

(완성)

옵션 설정하기

  • PanModalPresentable를 준수하고 여기서 여러가지의 computed property와 method를 정의해주면 여러가지 기능을 on/off하여 자유롭게 사용이 가능
// in PanModalPresentable ...

/// 스크롤될 뷰
var panScrollable: UIScrollView? { get }

/// 바텀시트의 시작 크기(= 작았을때의 크기)
var shortFormHeight: PanModalHeight { get }

/// 바텀시트 배경 색상 (디폴트: black 0.7 alpha)
var panModalBackgroundColor: UIColor { get }

/// 스와이프 다운해서 dismiss (디폴트: true)
var allowsDragToDismiss: Bool { get }

/// 탭해서 내리기 (디폴트: true)
var allowsTapToDismiss: Bool { get }

/// 라이프사이클
func panModalWillDismiss()
func panModalDidDismiss()
  • PanModal 구현 구조
    • *UITransitionView바로 위에 DimmedView가 있고, 아래쪽 바텀시트 영역으로 PanContainerView가 존재
    • PanContainerView에 위에서 presentPanModal로 띄운 ViewController가 존재

 

* 전체 코드: https://github.com/JK0369/ExPanModal

* 참고

https://github.com/slackhq/PanModal

Comments