기어가더라도 제대로

[UIkit-기초] Floating Panel 본문

UIKit 기초

[UIkit-기초] Floating Panel

Damagucci-juice 2024. 7. 22. 12:51

도입 상황

UIBottomSheetPresentater 를 사용하려고 했으나, iOS 15부터 지원이 된다고 하여, 다른 대안을 찾아보던 중 FloatingPanel 이라는 레포지토리가 유명하여 사용법을 익히기로 함

도입하는 패키지

  • SnapKit: UI 구현 편의
  • Then: UI 구현 편의
  • FloatingPanel: Bottom Sheet를 사용하기 위함

사용법

1. 뷰컨트롤러 안에 Botton Sheet를 항상 띄워 놓기

ParentViewController: 모달을 가지고 있을 배경 뷰컨트롤러

ChildViewController: 모달뷰 안에 구현될 뷰컨트롤러

// ParentViewController.swift
class ParentViewController: UIViewController {
    var fpc: FloatingPanelController!
    
    private func setupFloatingPanel(contentViewController: UIViewController) {
		    // FPC를 생성해서 선언
        fpc = FloatingPanelController()     
        // 컨텐츠로 사용할 ViewController를 fpc.set()으로 설정
        fpc.set(contentViewController: contentViewController)
        // fpc에서 관리하는 뷰들을 어느 뷰 컨트롤러에 추가할지 설정
        fpc.addPanel(toParent: self)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let contentVC = ChildViewController()
        setupFloatingPanel(contentViewController: contentVC)
        setupLayout()
    }

fpc.addPanel(toParent: **self**) : fpc에 의해 관리되는 뷰를 특정 뷰 컨트롤러에 추가, 뷰컨트롤러가 fpc를 변수로 들고 있다면 이것을 선언해도 좋음

self.addChild(fpc) : 컨트롤러를 현재 뷰컨트롤러에 자식 뷰컨트롤러로 넣는 기능, 뷰컨트롤러가 fpc를 변수로 들고있지 않고 FloatingPanelViewController를 상속 받은 뷰 컨트롤러를 상위 뷰 컨트롤러에 초기화할 때 사용함

2. Modal로 띄우기

숨겨져 있다가 트리거되면 올라오는 구조

기능

  • 버튼으로 나타나기:
  • 스와이프로 사라지기: 되긴하는데, 매끄럽지 못함
    • 패널만 추가하는게 아니라 특정 뷰컨을 FPC로 만들어서 아예 이 뷰 자체를 사라지게 만들기.
    • 엄청나다.
  • 버튼으로 사라지기: 매끄럽게 사라짐
class ModalViewController: UIViewController {

    private var fpc: FloatingPanelController!

    private let showModalButton = UIButton().then {
        $0.setTitle("Show Modal", for: .normal)
        $0.addTarget(
            self,
            action: #selector(showModal),
            for: .touchUpInside
        )
        $0.setTitleColor(.blue, for: .normal)
    }

    private let hideModalButton = UIButton().then {
        $0.setTitle("Hide Modal", for: .normal)
        $0.addTarget(
            self,
            action: #selector(hideModal),
            for: .touchUpInside
        )
        $0.setTitleColor(.blue, for: .normal)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        setupLayout()
        setupFPC()
    }

    func setupLayout() {
        view.backgroundColor = .white
        view.addSubview(showModalButton)
        view.addSubview(hideModalButton)

        showModalButton.snp.makeConstraints {
            $0.centerX.equalToSuperview()
            $0.top.equalToSuperview().offset(100)
        }

        hideModalButton.snp.makeConstraints {
            $0.centerX.equalToSuperview()
            $0.top.equalTo(showModalButton.snp.bottom).offset(25)
        }
    }

    func setupFPC() {
        // init
        fpc = FloatingPanelController()
        view.addSubview(fpc.view)
        
        // delegate
        fpc.delegate = self

        // size
        fpc.view.frame = self.view.bounds

        // 공식문서엔 옵셔널이라고 했는데, 넣으면 더 견고하게 할 수 있다고 해서 선언
        // Auto Layout
        fpc.view.snp.makeConstraints {
            $0.edges.equalToSuperview()
        }

				// 컨트롤러를 등록
        self.addChild(fpc)
    }

    @objc
    func showModal() {
        fpc.show(animated: true) {
            self.fpc.didMove(toParent: self)
        }
    }

    @objc
    func hideModal() {
        fpc.willMove(toParent: nil)
        fpc.hide(animated: true)
    }
}

참조

https://github.com/scenee/FloatingPanel?tab=readme-ov-file#features

Comments