๐ Delegate Pattern
Delegate ํจํด์ iOS์์ ์ ๋ง ๋ง์ด ์ฐ์ด์ง๋ง, ๋ญ์ง ๋ชจ๋ฅด๊ณ ๊ทธ๋ฅ ์ฝ๋๋ฅผ ์น๊ณ ์๊ธฐ ์ ์ผ ์ฌ์ด ๊ฒ๋ค ์ค ํ๋์ธ ๊ฒ ๊ฐ๋ค.
์๋ฅผ ๋ค์ด, UITableView๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์ UITableViewDataSource, UITableViewDelegate๋ฅผ ๋น์ฐํ๊ฒ ViewController์ ์ฑํํ๊ณค ํ์ง๋ง ์ ๊ผญ ํ ์ด๋ธ๋ทฐ๋ฅผ ๊ตฌํํ๊ธฐ ์ํด UITableViewDataSource๋ฅผ ์ฑํํด์ผ ํ๋์ง, UITableViewDataSource์ UITableViewDelegate๋ ์ ํ๋กํ ์ฝ๋ก ๊ตฌํ๋์ด ์๋์ง ๋ฑ๋ฑ์ ์ค๋ช ํ๋ ๊ฑด ํนํ๋ ์ด์ฌ์์๊ฒ ์ฝ์ง ์์ ์ผ์ด๋ค.
์ ํ์ ์ ๊ทธ๋ ๊ฒ๋ ๋ง์ UI์์๋ค์ Delegate ํจํด์ ์ฌ์ฉํ๊ณ ์๋ ๊ฑธ๊น
ํญ์ ์ดํดํ ๋ฏ ์ ํ๋ฏ, ์๋ ๋ฏ ๋ชจ๋ฅด๋ ๋ฏํ Delegate ํจํด์ ์ดํดํด๋ณด์
๐ก Delegate Pattern ์ด๋?
๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ๋ณดํต “๊ฐ์ฒด๊ฐ ์์ ์ ์ฑ ์์ ๋ค๋ฅธ ๊ฐ์ฒด์๊ฒ ์์(delegate)ํ๋ ๋์์ธ ํจํด”์ด๋ผ๊ณ ์ค๋ช ๋๋ค.
์๋ฅผ ๋ค์ด, ํ ์ด๋ธ๋ทฐ๋ ์ ์ ํญํ์ ๋ ์ด๋ค ํ๋์ ํ ์ง์ ๋ํ ์ฑ ์์ ๋ทฐ์ปจํธ๋กค๋ฌ์๊ฒ UITableViewDelegate๋ฅผ ์ฌ์ฉํด ์์ํ๋ค.
ํ ์ด๋ธ๋ทฐ ์ธ์๋ ์ฝ๋ ์ ๋ทฐ, ํ ์คํธํ๋ ๋ฑ ๋ง์ UI์์๋ค์ด ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ฌ์ฉํด ๋ค๋ฅธ ๊ฐ์ฒด์๊ฒ ์ฑ ์์ ์์ํ๊ณ ์๋ค.
์ ๊ตณ์ด “์์”์ ํ๋ ๊ฒ์ผ๊น?
๐ก UI์์์์์ Delegate Pattern
์ฌ์ฉ์๊ฐ ํ ์ด๋ธ๋ทฐ์ ์ ์ ํญํ๋ ์ํฉ์ ์๋ก ๋ค์ด๋ณด์.
์ ์ ํญํ๋ฉด ํ ์ด๋ธ๋ทฐ๋ ํญ ์ด๋ฒคํธ๋ฅผ ๋ฐ๋๋ค. ํ ์ด๋ธ๋ทฐ๊ฐ ํญ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ผ๋ฉด delegate์ didSelectRowAt ๋ฉ์๋๋ฅผ ์คํ์ํจ๋ค. ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ ๋ ์ด๋ค ํ๋์ ํ ๊ฒ์ธ์ง๋ฅผ delegate์๊ฒ ์์ํ ๊ฒ์ด๋ค.
๋ณดํต์ ViewController์ tableView.delegate = self์ ๊ฐ์ ์ฝ๋๋ฅผ ์์ฑํด ViewController self, ์ฆ ViewController์ ์ธ์คํด์ค ์์ ์ ์์์(delegate)๋ก ์ค์ ํ๊ณ , didSelectRowAt ๋ฉ์๋์ ์ ์ ํญํ์ ๋ ์ด๋ค ํ๋์ ํ ์ง๋ฅผ ์ ์ํ๋ค.
ํ ์ด๋ธ๋ทฐ๊ฐ ์์์ ๋ค ํ๋ฉด ๋ ๊ฒ ๊ฐ์๋ฐ ์ ๊ตณ์ด ๋ค๋ฅธ ๊ฐ์ฒด์๊ฒ ์์์ ํด์ฃผ๋ ๊ฑธ๊น?
๋ฐ๋ก ์ฐ๋ฆฌ๊ฐ UITableView์ ๋ด๋ถ ์ฝ๋๋ฅผ ์์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ์ด ํญ๋์์ ๋ ์ด๋ค ํ๋์ ํ ์ง๋ ์ํฉ์ ๋ฐ๋ผ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค. ํ์ง๋ง ์ฐ๋ฆฌ๋ ์ ํ์ด ์จ๊ฒจ๋์ ํ ์ด๋ธ๋ทฐ ์์ ์ฝ๋๋ฅผ ์์ ํ ์ ์๋ค. ๋ฐ๋ผ์ ๋ค๋ฅธ ๊ฐ์ฒด์์ ํด๋น ์ฝ๋๋ฅผ ์์ฑํด์ค ๋ค, ํ ์ด๋ธ๋ทฐ๊ฐ ๊ทธ ๊ฐ์ฒด์ ์ฝ๋๋ฅผ ํธ์ถํด์ค์ผ ํ๋ค.
์ด ๋ ํ ์ด๋ธ๋ทฐ์ ๊ฐ์ฒด๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ ๋ฐฉ์์ด Delegate Pattern์ธ ๊ฒ์ด๋ค.
Delegate Pattern์ ํตํด ๊ตฌํํ ๋ฐฉ์์ ๊ฐ๋ตํ ์์ํด๋ณธ๋ค๋ฉด ์๋์ ๊ฐ์ ๊ฒ์ด๋ค.
// Delegate Protocol
protocol UITableViewDelegate: AnyObject {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
}
// Delegating Object
class UITableView {
weak var delegate: UITableViewDelegate?
func didSelectedRowAt(indexPath: IndexPath) {
delegate?.tableView(self, didSelectRowAt: indexPath)
}
}
// Delegate Object
class ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath)
}
}
์ฆ, ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ํต์ฌ์ ๋ ๊ฐ์ฒด๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ, ๋๊ฐ์ ๊ฐ์ฒด๊ฐ ํจ์จ์ ์ผ๋ก ์ํตํ๋๋ก ๋์์ฃผ๋ ๊ฒ์ด๋ค.
UI์์์์๋ ์์ ์ฌ๋ก์์ ๋ณผ ์ ์๋ ๊ฒ์ฒ๋ผ
์ด๋ฒคํธ๋ฅผ ๋ฐ๋ ๊ฐ์ฒด(ex. UITableView)๊ฐ ๊ทธ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ด๋ค ํ๋์ ํ ์ง๋ฅผ delegate(ex. ViewController)์๊ฒ ์์ํ๋ค.
delegate๋ ์ด๋ค ๊ฐ์ฒด๊ฐ ์ด๋ฒคํธ๋ฅผ ๋ง๋ฌ์ ๋ ๊ทธ ๊ฐ์ฒด๋ฅผ ๋์ ํด์ ํ๋ํ๋ ๊ฐ์ฒด์ธ ๊ฒ!
๊ทธ๋์ ์ฃผ๋ก delegating ๊ฐ์ฒด๋ ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ ์ฒ๋ฆฌํ๋ responder ๊ฐ์ฒด(UIResponder๋ฅผ ์์ํ๋ ๊ฐ์ฒด)๋ค.
๐ก ๋ ๊ฐ์ฒด์ ์ํต์ ์ํ Delegate Pattern
UI์์์์ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ด์ฉํ๋ ์ด์ ๋ ์ฐ๋ฆฌ๊ฐ UI์์์ ๋ด๋ถ ์ฝ๋๋ฅผ ์์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ์ด์ผ๊ธฐํ๋ค.
๊ทธ๋ ๋ค๋ฉด ์ ํ์ด ์ด๋ฏธ ๊ตฌํํด๋์ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ด ์๋, ์ฐ๋ฆฌ๊ฐ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ํ์ฉํด ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฑด ์ด๋ค ๊ฒฝ์ฐ์ผ๊น?
์์์ ๋งํ๋ ๊ฒ์ฒ๋ผ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ํต์ฌ์ ๋ ๊ฐ์ฒด์ ์ํต์ด๋ฏ๋ก ๋ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ์ฐ๊ฒฐํด์ฃผ๊ณ ์ถ์ ๋ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ํ์ฉํด ๋ณผ ์ ์๋ค.
๋ณดํต์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ๊ฐ์ฒด์ ๊ทธ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๊ฐ์ฒด๊ฐ ๋ค๋ฅธ ๊ฒฝ์ฐ ๋ ๊ฐ์ฒด๋ฅผ ์ํตํ๊ฒ ํ๊ธฐ ์ํด ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ฐ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
๊ฐ๋จํ ์์
๋์์๋๋ฐ ๊ฐ์๊ธฐ ์์ด์คํฌ๋ฆผ์ด ๋จน๊ณ ์ถ์ด์ก๋ค. ๊ทธ๋ฐ๋ฐ ๋๊ฐ๊ธฐ๊ฐ ๋๋ฌด ๊ท์ฐฎ์์ ๋์์๊ฒ ์์ด์คํฌ๋ฆผ์ ์ฌ์ค๋ผ๊ณ ์ํค๋ ค๊ณ ํ๋ค.
class Me {
weak var delegate: Brother?
func ์ฌ๋๋ฉ๋ก๋() {
delegate?.buyIcecream()
}
}
class Brother {
let me = Me()
init() {
me.delegate = self
}
func buyIcecream() {
print("๋ฉ๋ก๋๋ฅผ ์๋ค")
}
}
๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ๋ํ ๋ง์ ์์ ์ฝ๋๋ค์ ๋ณด๋ฉด ํ๋กํ ์ฝ์ ๊ผญ ๊ตฌํํด๋์ง๋ง, ์์ ์ฝ๋์ฒ๋ผ ํ๋กํ ์ฝ ์์ด๋ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ๊ตฌํํ ์ ์๋ค.
์์ํ๋ ๊ฐ์ฒด์ ์์๋ฐ๋ ๊ฐ์ฒด, ์์ํ ํ๋๋ง ์ ์ํ๋ฉด ๋๋ค.
Me๋ ์์ด์คํฌ๋ฆผ์ ๋จน๊ณ ์ถ๋ค๋ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ง๋ง ๋ด๊ฐ ์ฌ๋ฌ ๊ฐ๊ธฐ ์ซ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ๊ฐ์ฒด, Brother์๊ฒ ์์ด์คํฌ๋ฆผ์ ์ฌ๋ ํ์๋ฅผ ์์ํ๋ค. Me๊ฐ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ฌ๋๋ฉ๋ก๋๋ฅผ ์คํ์์ผ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ฉด, ์ค์ง์ ์ผ๋ก๋ ๋ธ๋ฆฌ๊ฒ์ดํธ์ธ Brother๊ฐ ๋์ ์ผ์ ์ฒ๋ฆฌํด์ค๋ค.
๊ทธ๋ ๋ค๋ฉด ์ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ๋ง์ ์์ ์์ ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ ๊ฒ์ผ๊น?
ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ฉด, ์ฝ๋์ ์ ์ฐ์ฑ๊ณผ ์ฌ์ฌ์ฉ์ฑ์ ๋์ผ ์ ์๋ค. ์๋ฅผ ๋ค์ด๋ณด์.
protocol MeDelegate: AnyObject {
func buyIcecream()
}
class Me {
weak var delegate: Brother?
func ์ฌ๋๋ฉ๋ก๋() {
delegate?.buyIcecream()
}
}
class Brother: MeDelegate {
let me = Me()
init() {
me.delegate = self
}
func buyIcecream() {
print("๋ฉ๋ก๋๋ฅผ ์๋ค")
}
}
class Father: MeDelegate {
let me = Me()
init() {
me.delegate = self
}
func buyIcecream() {
print("๋ฉ๋ก๋๋ฅผ ์๋ค")
}
}
๋๋ ์ด์ ๋จ๋์๋ฟ๋ง ์๋๋ผ ์๋น ์๊ฒ๋ ์์ด์คํฌ๋ฆผ์ ์ฌ์ค๋ผ๊ณ ์ํฌ ์ ์๋ค.
MeProtocol์ ๋ง๋ค์ด๋์ผ๋ฉด Me๋ MeProtocol์ ์ฑํํ ๋๊ตฌ์๊ฒ๋ ์์ด์คํฌ๋ฆผ์ ์ฌ๋ ํ์๋ฅผ ์์ํ ์ ์๋ค.
์ด๋ ๊ฒ ํ๋กํ ์ฝ์ ์ฌ์ฉํด ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ๊ตฌํํ๋ฉด, ํจ์ฌ ๋ ์ ์ฐํ๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ๋ง๋ค ์ ์๋ค.
๐ก ์ค์ ๋ก ์ฑ์์ ์๋๋ฐฉ์
์ข ๋ ์ค์ ํ๋ก์ ํธ์์ ์ฌ์ฉํ ๋งํ ์๋ฅผ ์ดํด๋ณด์.
๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ฌ์ฉํ๋ ๊ฐ์ฅ ํํ ๊ฒฝ์ฐ๋ ๋ ๋ทฐ์ปจํธ๋กค๋ฌ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋๋ค. ์ฌ์ฉ์ ์ ๋ ฅ ๋ฑ์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ๋ทฐ์ปจํธ๋กค๋ฌ์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํด์ค์ผํ๋ ๋ทฐ์ปจํธ๋กค๋ฌ๊ฐ ์๋ก ๋ค๋ฅธ ๊ฒฝ์ฐ, ์๋ฅผ ๋ค์ด ์ฌ์ฉ์๊ฐ ํ๋กํ ์์ ์ฐฝ์์ ์ด๋ฆ, ์ ํ๋ฒํธ, ์ฃผ์ ๋ฑ์ ์ ๋ ฅํ๊ณ ํ์ธ ๋ฒํผ์ ๋๋ฌ ์ด์ ํ๋ฉด์ผ๋ก ๋์๊ฐ์ ๋ ์ ๋ ฅ๋ฐ์ ์ ๋ณด๋ฅผ ์ด์ ํ๋ฉด์ผ๋ก ์ ๋ฌํด ๋ณด์ฌ์ค์ผ ํ๋ ๊ฒฝ์ฐ๋ค์ ๋งํ๋ค.
์ข ๋ ๊ฐ๋จํ๊ฒ ๋ฒํผ์ ๋๋ ์ ๋ ๋ฌ ๋ชจ๋ฌ์์ ์ด๋ค ๋ฒํผ์ ๋๋ฅด๋๋์ ๋ฐ๋ผ ๊ฐ์์ง๋ ๊ณ ์์ด ์ด๋ฏธ์ง ๊ทธ๋ฆผ์ด ๋์ค๋๋ก ํ๋ ์ฑ์ ์๊ฐํด๋ณด์.
์ ์ฑ์ ๊ตฌํํ ์ฝ๋๋ ์๋์ ๊ฐ๋ค.
class FirstViewController: UIViewController, SecondViewControllerDelegate {
@IBOutlet weak var presentButton: UIButton!
@IBOutlet weak var animalImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func presentButtonTapped(_ sender: Any) {
let secondViewController = storyboard?.instantiateViewController(
withIdentifier: "SecondViewController"
) as! SecondViewController
secondViewController.delegate = self
self.present(secondViewController, animated: true)
}
func showImage(of animal: String) {
presentButton.isHidden = true
animalImageView.image = UIImage(named: animal)
}
}
protocol SecondViewControllerDelegate: AnyObject {
func showImage(of animal: String)
}
class SecondViewController: UIViewController {
weak var delegate: SecondViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func dogButtonTapped(_ sender: Any) {
delegate?.showImage(of: "dog")
self.dismiss(animated: true)
}
@IBAction func catButtonTapped(_ sender: Any) {
delegate?.showImage(of: "cat")
self.dismiss(animated: true)
}
}
FirstViewController๋ SecondViewDelegate ํ๋กํ ์ฝ์ ์ฑํํด SecondViewController์ ์์์๊ฐ ๋์๋ค.
SecondViewController๊ฐ ๊ฐ์์ง ๋๋ ๊ณ ์์ด ๋ฒํผ์ ๋๋ฅด๋ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ผ๋ฉด delegate์๊ฒ ๋๋ฌ์ง ๋ฒํผ์ ํด๋นํ๋ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ๋ฌ๋ผ๊ณ delegate์ showImage(of:)๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
delegate์ธ FirstViewController๋ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ํตํด ์ด๋ค ๋ฒํผ์ด ๋๋ ธ๋์ง์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ์ ํด๋นํ๋ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์๋ค.
๐ก ์ฃผ์ํ ์ : Strong Reference Cycle
์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด delegate ํ๋กํผํฐ๋ฅผ ์ ์ํ ๋ weak๋ก ์ ์ธํ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ ๊ฐ์ ํด๋์ค๋ฅผ ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ฌ์ฉํด ์ฐ๊ฒฐํ ๊ฒฝ์ฐ, Strong Reference Cycle์ด ์๊ธธ ์ ์์ผ๋ฏ๋ก ์ฃผ์ํด์ผ ํ๋ค.
delegating ๊ฐ์ฒด๋ delegate ํ๋กํผํฐ๋ฅผ ํตํด ์๋ ๊ฐ์ฒด๋ฅผ ๊ฐํ๊ฒ ์ฐธ์กฐํ๊ณ ์๊ณ , delegate ๊ฐ์ฒด ์ญ์ delegating ๊ฐ์ฒด๋ฅผ ๊ฐํ๊ฒ ์ฐธ์กฐํ๊ณ ์๊ธฐ ๋๋ฌธ์ Strong Reference Cycle์ด ์๊ธฐ๊ฒ ๋๋ ๊ฒ์ด๋ค.
delegate ํ๋กํผํฐ๋ฅผ ์ ์ธํ ๋๋ weak๋ก ์ ์ธํ๊ฑฐ๋ delegate๋ฅผ ๋ด๋นํ๋ ๋ณ๋์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฑ์ ๋ฐฉ์์ผ๋ก Strong Reference Cycle์ ํผํ ์ ์๋ค.
์๋ฅผ ๋ค์ด ๋๊ฐ์ ๋ทฐ์ปจํธ๋กค๋ฌ ์ฌ์ด์ ๋ฐ์ดํฐ ์ ๋ฌ์ ์ํด ๋ธ๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ฌ์ฉํ ๊ฒฝ์ฐ, delegating ๊ฐ์ฒด๋ delegateํ๋กํผํฐ๋ฅผ ํตํด ์๋ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๊ณ ์๊ณ , delegate ๊ฐ์ฒด๋ ํ๋กํผํฐ ๋ฑ์ ํตํด delegating ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๊ณ ์์ด ์๋ก๋ฅผ ๊ฐํ๊ฒ ์ฐธ์กฐํ๊ณ ์๊ฒ ๋๋ค.์ด๋ ๊ฒ ๋๋ฉด ํ๋์ ๋ทฐ์ปจํธ๋กค๋ฌ๊ฐ ํ๋ผ์ ์ฌ๋ผ์ง๋ค๊ณ ํด๋ ๋ฉ๋ชจ๋ฆฌ์์ ํด์ ๋์ง ์์ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ์๊ธด๋ค.
'๐ iOS > UIKit' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[iOS][UiKit] SnapKit ์ ๋ฆฌ ( inset/offset , translates~ ) (0) | 2023.06.09 |
---|---|
[iOS] Cell ๊ทธ๋ฆผ์ + ๋ฅ๊ธ๊ฒ ( shadow, cornerRadius ) (0) | 2023.05.23 |
[iOS]AutoLayout : TableView_Text_Dynamic_Cell (0) | 2023.04.04 |
[iOS] AutoLayout : TableView_Expand (0) | 2023.04.04 |
[iOS]AutoLayout : pop up _ animation (0) | 2023.04.02 |