Swift

[Swift] AutomaticDimension

yeggrrr๐Ÿผ 2024. 6. 3. 11:53
728x90

0
์™„์„ฑ ํ™”๋ฉด


์ข‹์€ ์ฝ”๋“œ ๊ตฌ์„ฑ์€ ์•„๋‹ ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ AutomaticDimension์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ ์–ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค!

๊ธฐ๋ณธ ์…‹ํŒ… ์ฝ”๋“œ๋Š” ์•„๋ž˜ ์ ‘์€ ๊ธ€๋กœ ์ ์–ด๋‘๊ฒ ์Šต๋‹ˆ๋‹ค :)

๋”๋ณด๊ธฐ

<VC>

import UIKit
import SnapKit

class ViewController: UIViewController {
    let tableView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        configureTableView()
    }
    
    func configureTableView() {
        view.addSubview(tableView)
        
        tableView.snp.makeConstraints {
            $0.edges.equalTo(view.safeAreaLayoutGuide)
        }
        
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(TestTableViewCell.self, forCellReuseIdentifier: TestTableViewCell.id)
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.id, for: indexPath) as? TestTableViewCell else { return UITableViewCell() }
        cell.tableVew = tableView        
        return cell
    }
}

<Cell>

import UIKit

class TestTableViewCell: UITableViewCell {
    let descriptionLabel = UILabel()
    let dropDownButton = UIButton()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        configureUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureUI() {        
        contentView.addSubview(descriptionLabel)
        contentView.addSubview(dropDownButton)
        
        let safeArea = contentView.safeAreaLayoutGuide
        descriptionLabel.snp.makeConstraints {
            $0.top.equalTo(safeArea).offset(20)
            $0.horizontalEdges.equalTo(safeArea).inset(20)
            $0.bottom.equalTo(dropDownButton.snp.top).offset(-10)
        }
        
        dropDownButton.snp.makeConstraints {
            $0.top.equalTo(descriptionLabel.snp.bottom)
            $0.centerX.equalTo(safeArea)
            $0.height.equalTo(30)
            $0.bottom.equalTo(safeArea)
        }
        
        descriptionLabel.textAlignment = .left
        descriptionLabel.textColor = .label
        descriptionLabel.font = .systemFont(ofSize: 15)
        descriptionLabel.numberOfLines = 2
        
        dropDownButton.setImage(UIImage(systemName: "chevron.down"), for: .normal)
        dropDownButton.tintColor = .black
    }
}

<TableViewCell ReusableProtocol>

protocol ReusableProtocol: AnyObject {
    static var id: String { get }
}

extension UIView: ReusableProtocol {
    static var id: String {
        return String(describing: self)
    }
}

1. ์…€ ๋†’์ด ์„ค์ •

๊ธฐ๋ณธ ์…‹ํŒ…์— ์ด์–ด์„œ ํ•ด์ค˜์•ผํ•˜๋Š” ๋ถ€๋ถ„์€ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ,
descriptionLabel์˜ ๊ธธ์ด ๋งŒํผ ์œ ๋™์ ์œผ๋กœ ์›€์ง์ผ ์ˆ˜ ์žˆ๋„๋ก TableViewCell ๋†’์ด๋ฅผ ์„ค์ •ํ•ด์ค˜์•ผํ•ฉ๋‹ˆ๋‹ค.

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }

์ด๋ ‡๊ฒŒ ๋†’์ด ๊ฐ’์„ ํŠน์ •ํ•ด์„œ ์ ์ง€ ์•Š๊ณ , UITableView.automaticDimension์œผ๋กœ ์ ์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

2. underDrop ๋ฒ„ํŠผ ์„ค์ •

TestTableViewCell ํŒŒ์ผ์—์„œ ๋งŒ๋“ค์—ˆ๋˜ underDropButton์— ๋Œ€ํ•œ action์„ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค. (addTarget)

class TestTableViewCell: UITableViewCell {
    let descriptionLabel = UILabel()
    let dropDownButton = UIButton()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        configureUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureUI() {
		...
        
        dropDownButton.addTarget(self, action: #selector(dropDownButtonClicked), for: .touchUpInside)
    }

    @objc func dropDownButtonClicked() {
		print("ํด๋ฆญ๋จ")
    }
}

์ž‘์„ฑ ํ›„, button์„ ํด๋ฆญํ–ˆ์„ ๋•Œ, "ํด๋ฆญ๋จ"์ด ํ”„๋ฆฐํŠธ ๋˜๋Š”์ง€ ํ™•์ธ!

3. ๋ฒ„ํŠผ ์ƒํƒœ ์„ค์ •

๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ, ํ•œ๋ฒˆ ๋” ํด๋ฆญํ–ˆ์„ ๋•Œ,
์ƒํƒœ๊ฐ€ ๊ณ„์† ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ toggle()์„ ํ™œ์šฉํ•ด์ฃผ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

var buttonState = false

์ฒ˜์Œ ์‹คํ–‰ํ–ˆ์„ ๋•Œ๋Š” ์ ‘ํ˜€์žˆ๋Š” ์ƒํƒœ์—ฌ์•ผํ•˜๋‹ˆ, false๋กœ ์„ ์–ธํ•ด๋‘๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  dropDownButtonClicked์— ํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

    @objc func dropDownButtonClicked() {
        buttonState.toggle()
        
        if buttonState {
        // descriptionLabel.text ์ „์ฒด ๊ธธ์ด๊ฐ€ ๋ชจ๋‘ ๋ณด์ผ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •
            descriptionLabel.numberOfLines = 0 
        } else {
        // ์ ‘์—ˆ์„ ๋•Œ, 2์ค„๋งŒ ๋ณด์ผ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •
            descriptionLabel.numberOfLines = 2
        }
    }

์ด๋ ‡๊ฒŒ ํ•ด์ฃผ๊ณ  ๋‚˜๋ฉด, ์ด์ œ Label์— ํ™•์ธ์šฉ์œผ๋กœ ์—„์ฒญ ๊ธด ๋ฌธ์žฅ์„ ๋„ฃ์–ด๋ด์•ผ๊ฒ ์ฃ ?
์ €๋Š” '์•„์ด์œ  - Love Wins All' ๊ฐ€์‚ฌ ์ผ๋ถ€๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.id, for: indexPath) as? TestTableViewCell else { return UITableViewCell() }
        cell.descriptionLabel.text = "Dearest, darling, my universe ๋‚  ๋ฐ๋ ค๊ฐ€ ์ค„๋ž˜? ๋‚˜์˜ ์ด ๊ฐ€๋‚œํ•œ ์ƒ์ƒ๋ ฅ์œผ๋ก  ๋– ์˜ฌ๋ฆด ์ˆ˜ ์—†๋Š” ๊ณณ์œผ๋กœ ์ €๊ธฐ ๋ฉ€๋ฆฌ from Earth to Mars ๊ผญ ๊ฐ™์ด ๊ฐ€์ค„๋ž˜? ๊ทธ๊ณณ์ด ์–ด๋””๋“  ์˜ค๋žœ ์™ธ๋กœ์›€, ๊ทธ ๋ฐ˜๋Œ€๋ง์„ ์ฐพ์•„์„œ ์–ด๋–ค ์‹ค์ˆ˜๋กœ ์ดํ† ๋ก ์šฐ๋ฆฌ๋Š” ํ•จ๊ป˜์ผ๊นŒ? ์„ธ์ƒ์—๊ฒŒ์„œ ๋„๋ง์ณ run on ๋‚˜์™€ ์ € ๋๊นŒ์ง€ ๊ฐ€์ค˜ my lover ๋‚˜์œ ๊ฒฐ๋ง์ผ๊นŒ? ๊ธธ ์žƒ์€ ์šฐ๋ฆฌ ๋‘˜ mm ๋ถ€์„œ์ง€๋„๋ก ๋‚˜๋ฅผ ๊ผญ ์•ˆ์•„ ๋” ์‚ฌ๋ž‘ํžˆ ๋‚ด๊ฒŒ ์ž… ๋งž์ถฐ lover Love is all, love is all Love, love, love, love ๊ฒฐ๊ตญ, ๊ทธ๋Ÿผ์—๋„ ์–ด์งธ์„œ ์šฐ๋ฆฌ๋Š” ์„œ๋กœ์ผ๊นŒ?"
        return cell
    }
}

 

4. TableView reload ํ•ด์ฃผ๊ธฐ

View๋Š” ํ•œ ๋ฒˆ ๊ทธ๋ ค์ง€๊ณ  ๋‚œ ํ›„์— ๋‹ค์‹œ ๊ทธ๋ ค๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•˜์ง€ ์•Š์œผ๋ฉด, ๋‹ค์‹œ ๊ทธ๋ ค์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ์œ„ ์ฝ”๋“œ์—์„œ ์‹คํ–‰ํ•˜๊ฒŒ๋˜๋ฉด, ์•„๋ฌด๋Ÿฐ ๋ณ€ํ™”๊ฐ€ ์—†์„๊ฒ๋‹ˆ๋‹ค.

๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ! ์ƒํƒœ๊ฐ€ ๋ณ€ํ™”ํ•ด์•ผํ•˜๋‹ˆ, ๋ฒ„ํŠผ ํด๋ฆญ Action์—์„œ reloadData๋ฅผ ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

var tableVew: UITableView?

์šฐ์„  Cell ํŒŒ์ผ์—์„œ ํ…Œ์ด๋ธ” ๋ทฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์œผ๋‹ˆ๊นŒ ์ด๋ ‡๊ฒŒ ์„ ์–ธํ•ด์ฃผ๊ณ ,

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.id, for: indexPath) as? TestTableViewCell else { return UITableViewCell() }
        cell.tableVew = tableView // ์ด๋ ‡๊ฒŒ
        cell.descriptionLabel.text = "Dearest, darling, my universe ๋‚  ๋ฐ๋ ค๊ฐ€ ์ค„๋ž˜? ๋‚˜์˜ ์ด ๊ฐ€๋‚œํ•œ ์ƒ์ƒ๋ ฅ์œผ๋ก  ๋– ์˜ฌ๋ฆด ์ˆ˜ ์—†๋Š” ๊ณณ์œผ๋กœ ์ €๊ธฐ ๋ฉ€๋ฆฌ from Earth to Mars ๊ผญ ๊ฐ™์ด ๊ฐ€์ค„๋ž˜? ๊ทธ๊ณณ์ด ์–ด๋””๋“  ์˜ค๋žœ ์™ธ๋กœ์›€, ๊ทธ ๋ฐ˜๋Œ€๋ง์„ ์ฐพ์•„์„œ ์–ด๋–ค ์‹ค์ˆ˜๋กœ ์ดํ† ๋ก ์šฐ๋ฆฌ๋Š” ํ•จ๊ป˜์ผ๊นŒ? ์„ธ์ƒ์—๊ฒŒ์„œ ๋„๋ง์ณ run on ๋‚˜์™€ ์ € ๋๊นŒ์ง€ ๊ฐ€์ค˜ my lover ๋‚˜์œ ๊ฒฐ๋ง์ผ๊นŒ? ๊ธธ ์žƒ์€ ์šฐ๋ฆฌ ๋‘˜ mm ๋ถ€์„œ์ง€๋„๋ก ๋‚˜๋ฅผ ๊ผญ ์•ˆ์•„ ๋” ์‚ฌ๋ž‘ํžˆ ๋‚ด๊ฒŒ ์ž… ๋งž์ถฐ lover Love is all, love is all Love, love, love, love ๊ฒฐ๊ตญ, ๊ทธ๋Ÿผ์—๋„ ์–ด์งธ์„œ ์šฐ๋ฆฌ๋Š” ์„œ๋กœ์ผ๊นŒ?"
        return cell
    }

์ด๋ ‡๊ฒŒ ํ• ๋‹นํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค. (์ด์ œ ๋ณด๋‹ˆ,, tableView์ด๋ฆ„์„ ๋„ˆ๋ฌด ์•„๋ฌด ์ƒ๊ฐ ์—†์ด ๋งŒ๋“ค์–ด๋†จ๋„ค์š”..ใ…Žใ…Ž...)

    @objc func dropDownButtonClicked() {
        buttonState.toggle()
        
        if buttonState {
            descriptionLabel.numberOfLines = 0
        } else {
            descriptionLabel.numberOfLines = 2
        }
        
        tableVew?.reloadData() // ์ด๋ ‡๊ฒŒ
    }

์ด๋ ‡๊ฒŒ ํ…Œ์ด๋ธ”๋ทฐ reloadData๋ฅผ ํ•ด์ฃผ๋ฉด ๋์ž…๋‹ˆ๋‹ค!

์ „์ฒด ์ฝ”๋“œ๋Š” ์•„๋ž˜์— ์ ‘์€๊ธ€๋กœ ๋„ฃ์–ด๋‘๊ฒ ์Šต๋‹ˆ๋‹ค. :)๐Ÿ˜€
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.๐Ÿ™‡๐Ÿป‍โ™€๏ธ


<์ „์ฒด์ฝ”๋“œ>

๋”๋ณด๊ธฐ

<ViewController>

import UIKit
import SnapKit

class ViewController: UIViewController {
    let tableView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        configureTableView()
    }
    
    func configureTableView() {
        view.addSubview(tableView)
        
        tableView.snp.makeConstraints {
            $0.edges.equalTo(view.safeAreaLayoutGuide)
        }
        
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(TestTableViewCell.self, forCellReuseIdentifier: TestTableViewCell.id)
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.id, for: indexPath) as? TestTableViewCell else { return UITableViewCell() }
        cell.tableVew = tableView
        cell.descriptionLabel.text = "Dearest, darling, my universe ๋‚  ๋ฐ๋ ค๊ฐ€ ์ค„๋ž˜? ๋‚˜์˜ ์ด ๊ฐ€๋‚œํ•œ ์ƒ์ƒ๋ ฅ์œผ๋ก  ๋– ์˜ฌ๋ฆด ์ˆ˜ ์—†๋Š” ๊ณณ์œผ๋กœ ์ €๊ธฐ ๋ฉ€๋ฆฌ from Earth to Mars ๊ผญ ๊ฐ™์ด ๊ฐ€์ค„๋ž˜? ๊ทธ๊ณณ์ด ์–ด๋””๋“  ์˜ค๋žœ ์™ธ๋กœ์›€, ๊ทธ ๋ฐ˜๋Œ€๋ง์„ ์ฐพ์•„์„œ ์–ด๋–ค ์‹ค์ˆ˜๋กœ ์ดํ† ๋ก ์šฐ๋ฆฌ๋Š” ํ•จ๊ป˜์ผ๊นŒ? ์„ธ์ƒ์—๊ฒŒ์„œ ๋„๋ง์ณ run on ๋‚˜์™€ ์ € ๋๊นŒ์ง€ ๊ฐ€์ค˜ my lover ๋‚˜์œ ๊ฒฐ๋ง์ผ๊นŒ? ๊ธธ ์žƒ์€ ์šฐ๋ฆฌ ๋‘˜ mm ๋ถ€์„œ์ง€๋„๋ก ๋‚˜๋ฅผ ๊ผญ ์•ˆ์•„ ๋” ์‚ฌ๋ž‘ํžˆ ๋‚ด๊ฒŒ ์ž… ๋งž์ถฐ lover Love is all, love is all Love, love, love, love ๊ฒฐ๊ตญ, ๊ทธ๋Ÿผ์—๋„ ์–ด์งธ์„œ ์šฐ๋ฆฌ๋Š” ์„œ๋กœ์ผ๊นŒ?"
        return cell
    }
}

<TestTableViewCell>

import UIKit

class TestTableViewCell: UITableViewCell {
    let descriptionLabel = UILabel()
    let dropDownButton = UIButton()
    var buttonState = false
    var tableVew: UITableView?
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        configureUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureUI() {
        contentView.addSubview(descriptionLabel)
        contentView.addSubview(dropDownButton)
        
        let safeArea = contentView.safeAreaLayoutGuide
        descriptionLabel.snp.makeConstraints {
            $0.top.equalTo(safeArea).offset(20)
            $0.horizontalEdges.equalTo(safeArea).inset(20)
            $0.bottom.equalTo(dropDownButton.snp.top).offset(-10)
        }
        
        dropDownButton.snp.makeConstraints {
            $0.top.equalTo(descriptionLabel.snp.bottom)
            $0.centerX.equalTo(safeArea)
            $0.height.equalTo(30)
            $0.bottom.equalTo(safeArea)
        }
        
        descriptionLabel.textAlignment = .left
        descriptionLabel.textColor = .label
        descriptionLabel.font = .systemFont(ofSize: 15)
        descriptionLabel.numberOfLines = 2
        
        dropDownButton.setImage(UIImage(systemName: "chevron.down"), for: .normal)
        dropDownButton.tintColor = .black
        
        dropDownButton.addTarget(self, action: #selector(dropDownButtonClicked), for: .touchUpInside)
    }
    
    @objc func dropDownButtonClicked() {
        buttonState.toggle()
        
        if buttonState {
            descriptionLabel.numberOfLines = 0
        } else {
            descriptionLabel.numberOfLines = 2
        }
        
        tableVew?.reloadData()
    }
}

<ReusableProtocol+>

import UIKit

protocol ReusableProtocol: AnyObject {
    static var id: String { get }
}

extension UIView: ReusableProtocol {
    static var id: String {
        return String(describing: self)
    }
}

 

728x90