서론
요즘에 아이폰 앱을 사용하다보면 간단한 후기 입력 폼으로 많이 보이는 모달이 하나 있는데요. 위처럼 보이는 전체화면이 아닌 화면의 절반정도 차지하는 모달입니다. 정확한 명칭이 어떤건지는 잘 모르겠지만.. 검색을 하다보니 관련된 라이브러리가 있더라구요.
Bulletin Board 라고 정의된 라이브러리가 있어서 간단하게 사용하고 싶으신 분들은 저 라이브러리를 사용하시면 좋을 것 같습니다.
해당 포스트에서는 라이브러리를 사용하지 않고, 좀더 커스터마징을 하고싶은 사람들을 위해 기본적인 동작방법을 적어놓은 포스트입니다.
구현방법
두개의 UIViewController를 사용하여 구현합니다.
모달을 띄우는 주체가 되는 컨트롤러는 부모 컨트롤러, Bulletin board가 있는 컨트롤러를 자식 컨트롤러로 정의하겠습니다. 부모 컨트롤러에서는 위 그림의 MainView와 BlackBg를 가지고 있고 자식 컨트롤러에서는 Bulletin board를 가지고 있습니다. 부모 컨트롤러에서 버튼을 누르면 까만 배경 뷰를 만들고 그 위에 자식 컨트롤러를 present시키는 방식입니다. 반대로 자식 컨트롤러에서 할 동작이 완료되어 dismiss시키면 부모 뷰의 배경뷰도 같이 사라지게 해야합니다.
이번에 만들어볼 녀석은 최상위에 있는 예시의 간단한 버전으로 소개해드리겠습니다. (디자인 적용 유무만 다른거니까요!)
부모 컨트롤러
// ViewController.swift
import UIKit
import Then
import SnapKit
class ViewController: UIViewController {
let button = UIButton().then {
$0.setTitle("Board 보기", for: .normal)
$0.setTitleColor(.black, for: .normal)
}
let bgView = UIView().then {
$0.backgroundColor = .black
$0.alpha = 0
}
static func instance() -> ViewController {
return ViewController(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// 뷰에 버튼 추가
view.addSubview(button)
button.snp.makeConstraints { (make) in
make.center.equalToSuperview()
}
// 이벤트 추가
button.addTarget(self, action: #selector(onTapBtn), for: .touchUpInside)
}
@objc
func onTapBtn() {
// BulletinBoard Modal 생성
let bulletinBoardVC = BulletinVC.instance()
bulletinBoardVC.delegate = self
addDim()
present(bulletinBoardVC, animated: true, completion: nil)
}
private func addDim() {
view.addSubview(bgView)
bgView.snp.makeConstraints { (make) in
make.edges.equalTo(0)
}
DispatchQueue.main.async { [weak self] in
self?.bgView.alpha = 0.2
}
}
private func removeDim() {
DispatchQueue.main.async { [weak self] in
self?.bgView.removeFromSuperview()
}
}
}
extension ViewController: BulletinDelegate {
func onTapClose() {
self.removeDim()
}
}
여기에서 가장 중요하게 봐야할 곳은 addDim()함수입니다.
private func addDim() {
// 뷰를 추가하고 autoLayout 설정
view.addSubview(bgView)
bgView.snp.makeConstraints { (make) in
make.edges.equalTo(0)
}
// 메인쓰레드를 사용해 추가한 뷰의 alpha값 설정
// 이렇게 하면 그림자가 fade로 적용됩니다.
DispatchQueue.main.async { [weak self] in
self?.bgView.alpha = 0.2
}
}
새로운 뷰 컨트롤러가 추가되기전에 부모 뷰에서 Dim뷰를 추가하는 함수입니다.
새로 추가되는 뷰는 전체화면이 아니라 화면의 일부만 차지하는 뷰이므로 부모뷰에서 추가한 dim이 배경으로 깔리게 됩니다.
추가되는 자식 컨트롤러(Dialog??)
// BulletinVC.swift
import UIKit
import Then
protocol BulletinDelegate: class {
func onTapClose()
}
class BulletinVC: UIViewController {
weak var delegate: BulletinDelegate?
let bgView = UIView().then {
$0.backgroundColor = .white
$0.layer.cornerRadius = 20
}
let closeBtn = UIButton().then {
$0.setTitle("댣기", for: .normal)
$0.setTitleColor(.black, for: .normal)
}
static func instance() -> BulletinVC {
return BulletinVC(nibName: nil, bundle: nil).then {
$0.modalPresentationStyle = .overFullScreen
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Setup view
view.backgroundColor = .clear
view.addSubview(bgView)
view.addSubview(closeBtn)
// Add Event
closeBtn.addTarget(self, action: #selector(onTapClose), for: .touchUpInside)
// Setup AutoLayout
bgView.snp.makeConstraints { (make) in
make.left.equalToSuperview().offset(10)
make.right.equalToSuperview().offset(-10)
make.bottom.equalToSuperview().offset(-30)
make.height.equalTo(200)
}
closeBtn.snp.makeConstraints { (make) in
make.center.equalTo(bgView)
}
}
@objc
func onTapClose() {
delegate?.onTapClose()
dismiss(animated: true, completion: nil)
}
}
추가되는 자식 컨트롤러에서는 프로토콜을 하나 구현해주었습니다.
protocol BulletinDelegate: class {
func onTapClose()
}
class BulletinVC: UIViewController {
...
// 버튼에 이벤트 추가
closeBtn.addTarget(self, action: #selector(onTapClose), for: .touchUpInside)
...
@objc
func onTapClose() {
delegate?.onTapClose()
dismiss(animated: true, completion: nil)
}
}
해당 Delegate함수는 닫기버튼을 터치했을때 호출이 됩니다. 이 함수는 부모 컨트롤러의 removeDim()함수를 호출하여 dim을 제거시켜줍니다. 자식 컨트롤러를 추가하기 전 부모컨트롤러에서 dim을 추가하였으므로 동일하게 부모컨트롤러에서 dim을 제거해줘야 하고, dim을 제거해야하는 순간은 자식 컨트롤러가 dismiss되었을때 입니다. 해당 순간을 파악하기 위해 자식 컨트롤러에 프로토콜을 구현하였습니다.
현재 생각난 방법은 이런방법으로 자연스럽게 나타내었는데 더 좋은 방법이 있다면 알려주세요!!
'iOS' 카테고리의 다른 글
📱도로명주소 API로 사용해보는 간단 Alamofire구현 (iOS API통신) (4) | 2020.06.01 |
---|---|
📱iOS Stretchy header 오토 레이아웃으로 구현하기 (Snapkit) (0) | 2020.05.31 |
[iOS/Swift5] 스토리보드 없이 횡스크롤 UICollectionView 만들기 (0) | 2020.02.08 |
[iOS/Swift] 선택된 셀이 표시되는 UICollectionView (0) | 2020.02.08 |
[iOS/Swift] 스토리보드 없이 UIScrollView 만들기 (using Snapkit) (2) | 2020.02.04 |