[iOS/UI kit] 스토리보드 없이 모달 구현하기 (modal without storyboard)
안녕하세요! 개발감자입니다🥔
저는 현재 스토리보드 없이 UI kit로 앱 개발 중입니다. 모달을 구현해야 하는 상황이지만, 구글링을 해본 결과 스토리보드없이 모달을 구현한 예제를 찾기가 어려웠습니다. 혹시나 저와 같은 상황일 분들을 위하여 예제를 공유합니다!
1. 모달을 부를 버튼 만들기
< 카테고리를 선택해주세요> 옆에 있는 빨간색 버튼을 클릭하면 모달을 불러오도록 구현해볼 것입니다.
일단 버튼을 구현해줍니다. 버튼의 isUserInteractionEnabled의 속성을 true로 설정하여 사용자와 상호작용할 수 있도록 합니다.
// 카테고리 선택 버튼 추가
lazy var categoryChooseButton: UIButton = {
let button = UIButton()
let arrowImage = UIImage(systemName:"pencil") // Replace "arrow_down" with the actual image name in your assets
button.setImage(arrowImage, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.isUserInteractionEnabled = true // 클릭 활성화
button.backgroundColor = UIColor.red
return button
}()
2. 버튼 클릭 시 모달을 부르는 함수 구현하기
button.addTarget(self, action: #selector(showCategoryModal), for: .touchUpInside) //클릭시 모달 띄우기
위의 함수를 1번의 버튼 구현한 부분에 추가하면, 버튼 클릭시 <showCategoryModal>함수를 실행하게 됩니다.
추가한 코드는 아래와 같습니다.
/ 카테고리 선택 버튼 추가
lazy var categoryChooseButton: UIButton = {
let button = UIButton()
let arrowImage = UIImage(systemName:"pencil") // Replace "arrow_down" with the actual image name in your assets
button.setImage(arrowImage, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.isUserInteractionEnabled = true // 클릭 활성화
button.backgroundColor = UIColor.red
button.addTarget(self, action: #selector(showCategoryModal), for: .touchUpInside) //클릭시 모달 띄우기
return button
}()
3. 모달 뷰 컨트롤러 생성하기
이제 showCategoryModal 함수를 구현하면 모달을 띄울 수 있게 됩니다.
@objc
private func showCategoryModal() {
print("Category Button clicked")// 버튼 클릭 표시
categoryChooseButton.backgroundColor = UIColor.green // 버튼 배경을 초록색으로 변경
let categoryModalVC = CategoryModalViewController()
categoryModalVC.modalPresentationStyle = .overCurrentContext // 모달이 전체 뷰를 덮도록 설정
// 모달을 띄우기
present(categoryModalVC, animated: true, completion: nil)
}
버튼을 클릭했다는 것을 파악하기 위하여 위에서 2번째까지의 코드를 작성하였습니다. 빨간색 버튼을 클릭했을 때에 초록색 버튼으로 바뀌고 콘솔에 버튼이 클릭됐다는 문장이 찍히게 됩니다. 그 나머지의 코드는 `CategoryModalViewController`를 생성하고, 해당 뷰 컨트롤러를 모달로 띄우기 위한 부분입니다. 각 줄에 대한 설명은 다음과 같습니다.
- `let categoryModalVC = CategoryModalViewController()`: `CategoryModalViewController`의 인스턴스를 생성합니다.
- `categoryModalVC.modalPresentationStyle = .overCurrentContext`: `modalPresentationStyle` 속성을 `.overCurrentContext`로 설정합니다. 이 설정은 모달이 현재 뷰 컨트롤러의 내용 위에 나타나도록 합니다. 즉, 모달이 전체 뷰를 덮게 됩니다.
- `present(categoryModalVC, animated: true, completion: nil)`: `present` 메서드를 사용하여 `categoryModalVC`를 현재의 뷰 컨트롤러 위에 모달로 띄웁니다. `animated` 매개변수를 `true`로 설정하면 모달이 애니메이션과 함께 나타납니다. `completion` 클로저는 모달 표시가 완료된 후에 실행될 코드를 지정할 수 있습니다. 이 경우에는 `nil`로 설정되어 있습니다.
이 코드를 통해 `CategoryModalViewController`가 모달로 나타나고, 해당 모달이 전체 화면을 덮도록 설정됩니다. 이후에 모달에서 카테고리를 선택하면 해당 모달이 닫히게 됩니다.
4. 모달 뷰 컨트롤러 구현하기
import UIKit
class CategoryModalViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let categories = ["Food", "Shopping", "Entertainment", "Utilities", "Transportation"]
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "categoryCell")
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0, alpha: 0.5)
setupTableView()
}
func setupTableView() {
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
tableView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
tableView.widthAnchor.constraint(equalToConstant: 200),
tableView.heightAnchor.constraint(equalToConstant: 300)
])
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "categoryCell", for: indexPath)
cell.textLabel?.text = categories[indexPath.row]
return cell
}
// MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCategory = categories[indexPath.row]
print("Selected category: \(selectedCategory)")
dismiss(animated: true, completion: nil)
}
}
이 코드는 카테고리를 보여주는 간단한 모달 뷰 컨트롤러인 `CategoryModalViewController`를 정의합니다. 각 부분에 대한 설명은 다음과 같습니다
- `categories`: 모달에 표시할 카테고리를 저장하는 문자열 배열입니다.
- `tableView`: `UITableView` 인스턴스를 선언하고, 이 뷰 컨트롤러의 메인 뷰에 추가합니다. `delegate`와 `dataSource`는 현재 뷰 컨트롤러 자체로 설정되어 있으며, `UITableView`의 기본 셀을 사용하기 위해 "categoryCell"이라는 식별자로 셀을 등록합니다.
- `viewDidLoad()`: 뷰 컨트롤러의 뷰가 로드되면 호출되는 메서드입니다. 여기서는 배경을 반투명하게 설정하고, `setupTableView()` 메서드를 호출하여 `tableView`를 초기화합니다.
- `setupTableView()`: `tableView`의 제약 조건을 설정하여 화면 중앙에 위치하도록 합니다.
- `tableView(_:numberOfRowsInSection:)`: 데이터 소스 메서드로, 섹션당 행의 수를 반환합니다. 여기서는 카테고리 배열의 항목 수를 반환합니다.
- `tableView(_:cellForRowAt:)`: 데이터 소스 메서드로, 각 행에 대한 셀을 반환합니다. 재사용 큐에서 셀을 가져와 카테고리 배열에서 해당하는 텍스트를 설정합니다.
- 'tableView(_:didSelectRowAt:)`: 델리게이트 메서드로, 사용자가 특정 행을 선택했을 때 호출됩니다. 선택한 카테고리를 출력하고 `dismiss(animated:completion:)` 메서드를 사용하여 모달을 닫습니다.
이제 이 코드를 사용하여 다른 뷰 컨트롤러에서 `CategoryModalViewController`를 모달로 띄우고, 사용자가 카테고리를 선택하면 모달이 닫히게 됩니다.