티스토리 뷰
UIMenu를 예제를 통해 사용해보자.
📢 StoryBoard를 사용하지 않고 코드로 구현!!
1️⃣ UI 만들기
UI는 다음과 같이 설정하였다.
자주 사용하는 CustonVerticalView와 CustomNextBtn은 따로 View로 뻬서 관리하고 VC로 가져오기만 하였다.
이렇게 자주 사용하는View나 UI 요소를 따로 관리하면 추후 코드 유지 보수 하는데 도움된다.
CreateFolderVC는 상속받은 CustomNavigationBar와 가져온 View와 ui 요소 그리고 UIMenu를 사용하기 위한 ui 요소들로 구성되어져 있다.
상속받거나 가져온 요소들을 제외한 folderButton, parentFolderLabel, selectedFolderLabel은 위의 view를 구현하기 위해서다.
- parentFolderLabel: "상위 폴더" label
- stackView: [selectedFolderLabel, folderButton]
- selectedFolderLabel: UIMenu에서 선택한 데이터
- folderButton: UIMenu 보여줄 버튼
import UIKit
import SnapKit
class CreateFolderVC: CustomNavigationBar{
let folderButton = UIButton()
let parentFolderLabel = UILabel()
let selectedFolderLabel = UILabel()
let folderNameInputView = CustomVerticalView(labelText: "폴더 이름", placeholder: "이름")
let createButton = CustomNextBtn(title: "폴더 만들기")
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
func initView(){
view.backgroundColor = .white
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.spacing = 8
stackView.distribution = .equalSpacing
stackView.layer.borderWidth = 1
stackView.layer.cornerRadius = 8
stackView.layer.borderColor = UIColor(named: "Gray3")?.cgColor
stackView.addArrangedSubview(selectedFolderLabel)
stackView.addArrangedSubview(folderButton)
selectedFolderLabel.text = "선택"
folderButton.setImage(UIImage(named: "category"), for: .normal)
parentFolderLabel.text = "상위 폴더"
parentFolderLabel.font = .boldSystemFont(ofSize: 18)
view.addSubview(parentFolderLabel)
view.addSubview(stackView)
view.addSubview(folderNameInputView)
view.addSubview(createButton)
parentFolderLabel.snp.makeConstraints { make in
make.height.equalTo(25)
make.leading.equalToSuperview().inset(16)
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(32)
}
selectedFolderLabel.snp.makeConstraints{make in
make.leading.equalToSuperview().inset(16)
}
stackView.snp.makeConstraints { make in
make.height.equalTo(56)
make.top.equalTo(parentFolderLabel.snp.bottom).offset(8)
make.leading.trailing.equalToSuperview().inset(16)
}
folderNameInputView.snp.makeConstraints{make in
make.top.equalTo(stackView.snp.bottom).offset(24)
make.leading.trailing.equalToSuperview().inset(16)
make.height.equalTo(88)
}
createButton.snp.makeConstraints{make in
make.bottom.equalTo(view.snp.bottom).offset(-50)
make.leading.trailing.equalToSuperview().inset(16)
}
}
}
- CustomNavigationBar
또한, Navigation Bar도 VC로 만들어놓고 상속받아 사용하도록 하여 유지보수를 하는데 도움이 되었다.
이런 ui의 Navigation Bar는 많이 사용해서 상속받아서 사용하면 쉽고 빠르게 코드를 구현할 수 있다.
class CustomNavigationBar: UIViewController {
private var titleLabel: UILabel!
private var closeButton: UIButton!
private var currentTitle = ""
init(title: String) {
super.init(nibName: nil, bundle: nil)
setupNavigationBar(title: title)
currentTitle = title
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func setupNavigationBar(title: String) {
// X button
closeButton = UIButton(type: .system)
closeButton.setImage(UIImage(named: "close_icon"), for: .normal)
closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside)
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: closeButton)
// TitleView
titleLabel = UILabel()
titleLabel.text = title
titleLabel.textColor = .black
titleLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
titleLabel.sizeToFit()
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: titleLabel.frame.width, height: titleLabel.frame.height))
titleView.addSubview(titleLabel)
self.navigationItem.titleView = titleView
}
@objc func closeButtonTapped() {
}
}
- CustonVerticalView
import UIKit
import SnapKit
class CustomVerticalView: UIView {
var titleLabel: UILabel = {
let label = UILabel()
label.font = .boldSystemFont(ofSize: 18)
return label
}()
var textInputField = UITextField()
init(labelText: String, placeholder: String ) {
super.init(frame: .zero)
titleLabel.text = labelText
textInputField.placeholder = placeholder
setupViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
addSubview(titleLabel)
addSubview(textInputField)
textInputField.font = .systemFont(ofSize: 14)
textInputField.layer.borderWidth = 1
textInputField.layer.cornerRadius = 8
textInputField.layer.borderColor = UIColor(named: "Gray3")?.cgColor
textInputField.leftView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 16.0, height: 0.0))
textInputField.leftViewMode = .always
titleLabel.snp.makeConstraints { make in
make.height.equalTo(25)
make.top.equalTo(self.snp.top).offset(10)
}
textInputField.snp.makeConstraints { make in
make.height.equalTo(56)
make.top.equalTo(titleLabel.snp.bottom).offset(8)
make.leading.trailing.equalToSuperview()
}
}
}
- CustomNextBtn
import Foundation
import UIKit
class CustomNextBtn: UIButton {
init(title: String) {
super.init(frame: .zero)
setTitle(title, for: .normal)
titleLabel?.font = UIFont.systemFont(ofSize: 14)
setTitleColor(.white, for: .normal)
backgroundColor = UIColor(named: "Gray3")
layer.cornerRadius = 5
snp.makeConstraints { make in
make.height.equalTo(55)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
만약 이런 코드들을 한 VC에 다 때려넣으면 코드가 진짜,,, 엄청 난리 나겠지 ㅎㅎㅎ
그래서 이렇게 최대한 view와 ui 요소들을 VC로부터 분리하여 사용하고자 한다.
2️⃣ UIMenu 적용
folderButton 클릭시 작동할 액션 함수를 지정해준다.
folderButton.addTarget(self, action: #selector(showMenu), for: .touchUpInside)
- showMenu()
@objc private func showMenu() {
let dataArray = ["data1", "data2"] // data 설정
var menuItems = [UIAction]()
for data in dataArray {
let action = UIAction(title: data) { _ in
self.selectedFolderLabel.text = data
}
menuItems.append(action)
}
let menu = UIMenu(title: "title", children: menuItems)
self.folderButton.menu = menu
self.folderButton.showsMenuAsPrimaryAction = true
}
- UIAction
UIMenu를 설정하는데 UIAction이 왜 필요한가 의문이었다.
찾아보니 iOS 13 이상부터는 UIAction을 사용하여 메뉴 및 버튼의 동작을 정의할 수 있다고 한다.
클로저를 통해 동작이 수행될 때 실행할 코드를 지정할 수 있고, 이를 통해 메뉴 아이템의 동작을 구성할 수 있다고,,,
암튼 사용해야한다고 한다.
var menuItems = [UIAction]()
for data in dataArray {
let action = UIAction(title: data) { _ in
self.selectedFolderLabel.text = data
}
menuItems.append(action)
}
배열의 data들 다 가져와서 UIAction으로 만들고 menuItems에 추가해준다. 그럼 해당 데이터가 선택되었을 때 selectedFolderLabel의 text가 바뀌게 된다.
let menu = UIMenu(title: "title", children: menuItems)
self.folderButton.menu = menu
self.folderButton.showsMenuAsPrimaryAction = true
공식문서에서 title, image, identifier 등등 사용할 수 있다는데 난 title과 children만 사용하였다.
children이 이제 UIMenuElement를 넣는 거여서 menuItems를 넣어준다.
그러면 생성된 menu가 folderButton에 할당되고, showsMenuAsPrimaryAction을 true로 해준다.
여기까지해야 버튼을 누르면 메뉴가 나오면서 위에서 설정한 dataArray의 값들이 나온다.
'스위프트 > UIKit' 카테고리의 다른 글
[UIKit] - NavigationBar에 Item 넣기 (0) | 2024.02.10 |
---|---|
[Swift] - preview 보는 법 (0) | 2023.12.10 |
[UIKit] - 카카오 로그인 (0) | 2023.12.02 |
[UIKit] Collection View 검색 기능 (0) | 2023.11.06 |
[UIKit] storyboard없이 Navigation controller (0) | 2023.11.05 |