inblog logo
|
๐ŸŽ
    iOS

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

    Jan 07, 2024
    [UIKit] Custom Alert ์ œ์ž‘(feat. Builder Pattern)
    Builder Pattern1. Builder ๊ตฌํ˜„2. Concrete Builder ๊ตฌํ˜„ 3. Product ๊ตฌ์„ฑ4. ์ตœ์ข… ๊ฒฐ๊ณผ๋ฌผ

    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

    ๐ŸŽ

    RSSยทPowered by Inblog