[UIKit] Custom Alert ์ œ์ž‘(feat. Builder Pattern)

Jan 07, 2024
[UIKit] Custom Alert ์ œ์ž‘(feat. Builder Pattern)

Builder Pattern

  • ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ๊ตฌ์ถ•ํ•˜๋Š” ์ƒ์„ฑ ๋””์ž์ธ ํŒจํ„ด
 
์‚ฌ์šฉ ์ด์œ 
  • alert์˜ ๊ฒฝ์šฐ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ UI๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ์ฆ‰, ์–ด๋–ค Case์—๋Š” โ€˜ํ™•์ธโ€™, โ€˜์ทจ์†Œโ€™ ๋ฒ„ํŠผ ๋ชจ๋‘ ํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์–ด๋–ค Case์—๋Š” โ€˜ํ™•์ธโ€™ ๋ฒ„ํŠผ๋งŒ ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ, ์ƒํ™ฉ์— ๋”ฐ๋ผ ์กฐํ•ฉ๋  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ ์ด Builder Pattern์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•˜๋‹ค.
 
๊ธฐ๋ณธ ๊ตฌ์กฐ
  • ๋นŒ๋” ํŒจํ„ด์€ 4๊ฐ€์ง€ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„๋‹ค.
  1. Builder: ๊ฐ์ฒด์˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ •์˜ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค
  1. Concrete Builder: ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ๊ฒฐ๊ณผ๋ฌผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์„ ์ •์˜
  1. Director: ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์„ ์ •์˜
  1. Product: ๋นŒ๋”๋ฅผ ํ†ตํ•ด์„œ ์ƒ์„ฑ๋˜๋Š” ์ตœ์ข… ๊ฐ์ฒด
 
์žฅ์ 
  • ๋ถ„๋ฆฌ๋œ ๊ตฌ์ถ• ๊ณผ์ •: ๊ฐ์ฒด์˜ ๊ตฌ์ถ•๊ณผ ํ‘œํ˜„์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑ
  • ์žฌ์‚ฌ์šฉ์„ฑ: ๋™์ผํ•œ ๊ตฌ์ถ• ๊ณผ์ •์—์„œ ๋‹ค์–‘ํ•œ ํ‘œํ˜„ ๊ฐ€๋Šฅ
  • ๊ฐ€๋…์„ฑ: ๊ฐ ๋‹จ๊ณ„๊ฐ€ ๋ช…ํ™•ํžˆ ์ •์˜๋˜์–ด ์žˆ์–ด, ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์ง„๋‹ค.
 
๋‹จ์ 
  • ๋ณต์žก์„ฑ ์ฆ๊ฐ€: ๋‹จ์ˆœํ•œ ๊ฐ์ฒด์— ๋นŒ๋” ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ณต์žก์„ฑ์„ ์ฆ๋Œ€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

1. Builder ๊ตฌํ˜„

  • Builder๋Š” ๊ฐ์ฒด์˜ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์ •์˜ํ•˜๋Š” Class์ด๋‹ค.
  • Alert์˜ ๊ฒฝ์šฐ, title, message, action๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธ ์š”์†Œ๋ฅผ ๊ฐ€์ง„๋‹ค.
  • ๋”ฐ๋ผ์„œ, ์œ„์™€ ๊ฐ™์€ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ protocol์„ ํ™œ์šฉํ•ด ์ •์˜ํ•ด์ค€๋‹ค.
    • // // Protocol+Builder.swift // AlertBuilderPattern // // Created by Dongwan Ryoo on 1/7/24. // import Foundation //class์—์„œ๋งŒ ์ค€์ˆ˜ํ•  ์ˆ˜ ์žˆ์Œ protocol Builder: AnyObject { //alert์˜ title๊ณผ message var title: String? { get } var message: String? { get } //alert์˜ title, message, ConfirmAction์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฉ”์„œ๋“œ func setTitle(_ text: String) -> Self func setMessage(_ text: String) -> Self func addConfirmAction(_ text: String, action: (() -> Void)?) -> Self }

2. Concrete Builder ๊ตฌํ˜„

 
  • Builder๋ฅผ ๊ตฌํ˜„ํ–ˆ์œผ๋‹ˆ, ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฑ„ํƒํ•œ Concrete Builder๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.
  • Concrete Builder์˜ ํ•ต์‹ฌ์€ 1)์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ํ•˜๊ณ , 2)๊ฒฐ๊ณผ๋ฌผ์„ ์ •์˜ํ•˜๊ณ , 3)์ด๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋ฐฉ์‹์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

  1. ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„
  • Builder protocol์„ ์ฑ„ํƒํ•œ๋‹ค.
    • import Foundation class AlertConcreteBuilder: Builder { var title: String? var message: String? func setTitle(_ text: String) -> Self { } func setMessage(_ text: String) -> Self { } func addConfirmAction(_ text: String, action: (() -> Void)?) -> Self { } }

2)๊ฒฐ๊ณผ๋ฌผ์„ ์ •์˜
  • ๋ฐ˜ํ™˜ํ•  ๊ฒฐ๊ณผ๋ฌผ์€ Customํ•œ AlertController์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ณ„๋„์˜ CustomAlertController ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ค€๋‹ค.
    • // // CustomAlertController.swift // AlertBuilderPattern // // Created by Dongwan Ryoo on 1/7/24. // import UIKit class CustomAlertController: UIViewController { }
  • ๊ฒฐ๊ณผ๋ฌผ(Alert)์— ๋“ค์–ด๊ฐˆ UI ๋“ฑ์„ ์ •์˜ํ•ด์ค€๋‹ค.
    • import UIKit import SnapKit class CustomAlertController: UIViewController { private lazy var alertView = { let view = UIStackView() view.layer.cornerRadius = 16 view.backgroundColor = .bgGrey view.axis = .vertical view.alignment = .center return view }() private lazy var titleLabel = { let view = UILabel() view.font = UIFont.systemFont(ofSize: 16, weight: .bold) view.textAlignment = .center return view }() private lazy var messageLabel = { let view = UILabel() view.font = UIFont.systemFont(ofSize: 12) view.textAlignment = .center return view }() private lazy var confirmButton = { let view = UIButton() view.setTitleColor(.white, for: .normal) view.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold) view.backgroundColor = .mainBlue view.layer.cornerRadius = 16 return view }() override func viewDidLoad() { addTarget() layouts() } } extension CustomAlertController { func layouts() { view.addSubview(alertView) alertView.snp.makeConstraints { $0.width.equalTo(312) $0.center.equalToSuperview() } alertView.addArrangedSubview(titleLabel) titleLabel.snp.makeConstraints { $0.top.equalToSuperview().offset(24) $0.width.equalTo(276) $0.centerX.equalToSuperview() } alertView.addArrangedSubview(messageLabel) alertView.setCustomSpacing(12, after: titleLabel) messageLabel.snp.makeConstraints { $0.width.equalTo(276) $0.centerX.equalToSuperview() } alertView.addArrangedSubview(confirmButton) alertView.setCustomSpacing(24, after: messageLabel) confirmButton.snp.makeConstraints { $0.centerX.equalToSuperview() $0.width.equalTo(276) $0.height.equalTo(44) } } } extension CustomAlertController { func addTarget() { confirmButton.addTarget(self, action: #selector(confirmButtonTapped), for: .touchUpInside) } @objc func confirmButtonTapped() { dismiss(animated: true) } }
  • ๊ฒฐ๊ณผ๋ฌผ์— ๋“ค์–ด๊ฐˆ title, massage๋ฅผ ๋ฐ›์„ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    • class CustomAlertController: UIViewController { var alertTitle: String? var alertMessage: String? private lazy var alertView = { let view = UIStackView() view.layer.cornerRadius = 16 view.backgroundColor = .bgGrey view.axis = .vertical view.alignment = .center return view }() . . . //๊ธฐ์กด ์ฝ”๋“œ
  • ๋ฒ„ํŠผ์˜ Action์„ ์ •์˜ํ•  ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.
  • ์ด๋•Œ, ๋ฒ„ํŠผ์— ๋“ค์–ด๊ฐˆ ํ…์ŠคํŠธ์™€ Action์„ ๊ฐ™์ด ์ •์˜ํ•ด์ค€๋‹ค.
    • struct Action { var text: String? var action: (()-> Void)? }
  • CustomAlertController์— action ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.
    • class CustomAlertController: UIViewController { var alertTitle: String? var alertMessage: String? var confirmAction: Action? private lazy var alertView = { let view = UIStackView() view.layer.cornerRadius = 16 view.backgroundColor = .bgGrey view.axis = .vertical view.alignment = .center return view }() . . . //๊ธฐ์กด ์ฝ”๋“œ
  • ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌ์„ฑ ์š”์†Œ(title, message, action)๋ฅผ UI์— ์ ์šฉํ•ด์ค€๋‹ค.
    • private lazy var titleLabel = { let view = UILabel() view.font = UIFont.systemFont(ofSize: 16, weight: .bold) view.textAlignment = .center view.text = alertTitle //Title return view }() private lazy var messageLabel = { let view = UILabel() view.font = UIFont.systemFont(ofSize: 12) view.textAlignment = .center view.text = alertMessage //Message return view }() private lazy var confirmButton = { let view = UIButton() view.setTitleColor(.white, for: .normal) view.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold) view.backgroundColor = .mainBlue view.layer.cornerRadius = 16 view.setTitle(confirmAction?.text, for: .normal) //Button Title return view }() . . . @objc func confirmButtonTapped() { confirmAction?.action?() //Button Action dismiss(animated: true) }
  • ์ง€๊ธˆ๊นŒ์ง€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ๋ฐ˜ํ™˜ํ•  ๊ฒฐ๊ณผ๋ฌผ(CustomAlertController)์„ ์ •์˜ํ–ˆ๋‹ค.
  • ์ด์ œ, AlertConcreteBuilder๋กœ ๋Œ์•„๊ฐ€์„œ ๊ฒฐ๊ณผ๋ฌผ์„ ๋ฐ˜ํ™˜ํ•  ๋ฐฉ์‹์„ ์ •์˜ํ•˜๋ฉด ๋œ๋‹ค.

  1. ๊ฒฐ๊ณผ๋ฌผ ๋ฐ˜ํ™˜ ๋ฐฉ์‹ ์ •์˜
  • AlertConcreteBuilder๋กœ ๋Œ์•„๊ฐ€์„œ ์ •์˜๋œ ๊ฒฐ๊ณผ๋ฌผ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.
    • class AlertConcreteBuilder: Builder { var title: String? var message: String? private let alertViewController = CustomAlertController() func setTitle(_ text: String) -> Self { } func setMessage(_ text: String) -> Self { } func addConfirmAction(_ text: String, action: (() -> Void)?) -> Self { } }
  • Alert์˜ ๊ฒฝ์šฐ ํŠน์ • ViewController๋ฅผ ๋ฃจํŠธ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— RootViewController๋„ ํ•˜๋‚˜ ์ •์˜ํ•ด์ค€๋‹ค.
    • class AlertConcreteBuilder: Builder { var title: String? var message: String? private let alertViewController = CustomAlertController() private var rootViewController: UIViewController? func setTitle(_ text: String) -> Self { } func setMessage(_ text: String) -> Self { } func addConfirmAction(_ text: String, action: (() -> Void)?) -> Self { } }
  • ํ™•์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ์˜ ์•ก์…˜๋„ ์ •์˜ํ•ด์ค€๋‹ค.
    • class AlertConcreteBuilder: Builder { private var title: String? private var message: String? private var addConfirmAction: Action? private let alertViewController = CustomAlertController() private var rootViewController: UIViewController? func setTitle(_ text: String) -> Self { } func setMessage(_ text: String) -> Self { } func addConfirmAction(_ text: String, action: (() -> Void)?) -> Self { } }
  • AlertConcreteBuilder๋ฅผ ๊ฑฐ์ณ ๊ฒฐ๊ณผ๋ฌผ์— ์ „๋‹ฌํ•  ์š”์†Œ๋“ค์„ ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์— ๊ตฌํ˜„ํ•ด์ค€๋‹ค.
    • class AlertConcreteBuilder: Builder { var title: String? var message: String? private var addConfirmAction: Action? private let alertViewController = CustomAlertController() private var rootViewController: UIViewController? func setTitle(_ text: String) -> Self { title = text return self } func setMessage(_ text: String) -> Self { message = text return self } func addConfirmAction(_ text: String, action: (() -> Void)?) -> Self { addConfirmAction?.text = text addConfirmAction?.action = action return self } }
  • rootViewController๋ฅผ ์ •์˜ํ•  ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.
  • ๋งˆ์ง€๋ง‰์œผ๋กœ present() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒฐ๊ณผ๋ฌผ์„ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.
    • class AlertConcreteBuilder: Builder { var title: String? var message: String? private var addConfirmAction: Action? private let alertViewController = CustomAlertController() private var rootViewController: UIViewController? init(_ viewController: UIViewController) { rootViewController = viewController } func setTitle(_ text: String) -> Self { title = text return self } func setMessage(_ text: String) -> Self { message = text return self } func addConfirmAction(_ text: String, action: (() -> Void)?) -> Self { addConfirmAction?.text = text addConfirmAction?.action = action return self } @discardableResult func present() -> Self { alertViewController.alertTitle = title alertViewController.alertMessage = message alertViewController.confirmAction = addConfirmAction alertViewController.modalPresentationStyle = .overFullScreen alertViewController.modalTransitionStyle = .crossDissolve rootViewController?.present(alertViewController, animated: true) return self } }

3. Product ๊ตฌ์„ฑ

  • Director๋Š” ์ง€๊ธˆ ์ƒํ™ฉ์—์„œ๋Š” ๋ณต์žก๋„๋งŒ ๋Š˜๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑ X
  • ViewController์— Product ์ƒ์„ฑ
// // ViewController.swift // AlertBuilderPattern // // Created by Dongwan Ryoo on 1/7/24. // import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(true) showAlert() } func showAlert() { AlertConcreteBuilder(self) .setTitle("์•Œ๋ฆผ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.") .setMessage("๊ถŒํ•œ์„ ํ™•์ธํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.") .addConfirmAction("ํ™•์ธ") { print("ํ™•์ธ ๋ฒ„ํŠผ ํด๋ฆญ") } .present() } }

4. ์ตœ์ข… ๊ฒฐ๊ณผ๋ฌผ

notion image
Share article
RSSPowered by inblog