๐Ÿ”ฅย ๊ตฌํ˜„ํ•œ ๋ทฐ

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-21 แ„‹แ…ฉแ„’แ…ฎ 5.16.06.png

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-21 แ„‹แ…ฉแ„’แ…ฎ 5.17.36.png

์ €๋Š” ์ปค๋ฆฌํ˜๋Ÿผ ๋ฉ”์ธ๋ทฐ(์™ผ์ชฝ ์‚ฌ์ง„)์™€ ์ฃผ์ฐจ๋ณ„ ๋ฆฌ์ŠคํŠธ๋ทฐ(์˜ค๋ฅธ์ชฝ ์‚ฌ์ง„)๋ฅผ ๋งก์•˜์Šต๋‹ˆ๋‹ค.

  1. ์ปค๋ฆฌํ˜๋Ÿผ๋ทฐ

์ปค๋ฆฌํ˜๋Ÿผ๋ทฐ๋ฅผ ๋ณด๋ฉด API ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ํ•ด์„œ ์œ ์ €์˜ ์ฃผ์ฐจ, ์ผ์ˆ˜, D-Day, ๋กœ๋ (LottieAnimationView)์˜ progress ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋กœ๋  ์•„๋ž˜์— ํ…Œ์ด๋ธ” ๋ทฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ฒ˜์Œ ์ปค๋ฆฌํ˜๋Ÿผ ๋ทฐ์— ๋“ค์–ด ์™”์„ ๋•Œ, ์œ ์ € ์ฃผ์ฐจ์— ํ•ด๋‹นํ•˜๋Š” cell์„ ๊ฐ€์žฅ ์ƒ๋‹จ์— ๋ณด์—ฌ์ฃผ๊ณ  ์•ˆ์— ์žˆ๋Š” ๋‚ด์šฉ์„ ํŽผ์ณ์„œ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ›์•„์˜จ ์œ ์ €์˜ ํ•ด๋‹น ์ฃผ์ฐจ์— ๋”ฐ๋ผ scrollToRow๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ๋‹จ์— ์œ„์น˜ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ cell์ด ํŽผ์ณ์ ธ ์žˆ๋Š” ๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด cell์˜ ์ •๋ณด๋“ค์„ stackView์•ˆ์— ๋„ฃ์–ด์„œ stackView๋ฅผ isHidden ์‹œํ‚ค๋ฉด์„œ expandable cell์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํŽผ์ณ์ ธ ์žˆ๋Š” cell์˜ ์ฃผ์ฐจ๋ผ๋ฒจ์„ ๋นจ๊ฐ›๊ฒŒ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํŽผ์ณค์„ ๋•Œ ์ด๋ฏธ์ง€ ์œ„์— ์žˆ๋Š” ๋ฒ„ํŠผ์„ ํ†ตํ•ด โ€˜์ฃผ์ฐจ๋ณ„ ๋ฆฌ์ŠคํŠธโ€™๋ทฐ๋กœ ์ด๋™์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. cell์•ˆ์— ์žˆ์–ด์„œ delegate๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

  1. ์ฃผ์ฐจ๋ณ„ ๋ฆฌ์ŠคํŠธ ๋ทฐ

์ปค๋ฆฌํ˜๋Ÿผ๋ทฐ์—์„œ ์›ํ•˜๋Š” ์ฃผ์ฐจ๋ฅผ ์„ ํƒํ•ด์„œ ์ฃผ์ฐจ๋ณ„ ๋ฆฌ์ŠคํŠธ๋ทฐ๋กœ ๋„˜์–ด์˜ต๋‹ˆ๋‹ค. ์ด ๋ทฐ๋Š” ์ „์ฒดํ™”๋ฉด์ด ์–‘ ์˜†์œผ๋กœ ๋„˜์–ด๊ฐ€์•ผ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ปฌ๋ ‰์…˜๋ทฐ ์•ˆ์— ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ ๋„ฃ๋Š” ๋ฐฉ์‹์œผ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ—ค๋”๋ทฐ๋ฅผ ๋”ฐ๋กœ ๋„ฃ์ง€ ์•Š๊ณ  ํ—ค๋”๋ทฐ๋ฅผ row0์— ๋„ฃ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ทฐ์—์„œ๋Š” API๋กœ ์•„ํ‹ฐํด ์ •๋ณด๋“ค์„ ๊ฐ€์ ธ์™€ ํ…Œ์ด๋ธ”๋ทฐ cell์— ๋ฟŒ๋ ค์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ API ์—ฐ๊ฒฐํ•ด์„œ ๋ถ๋งˆํฌ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ๋„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํƒœ๊ทธ๊ฐ’๋“ค์ด ๋ฐฐ์—ด๋กœ ๋“ค์–ด์™€์„œ ๊ฐ ํƒœ๊ทธ ์ค‘๊ฐ„์ค‘๊ฐ„์— join์œผ๋กœ ์ ์„ ๋„ฃ์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ฒ˜์Œ ์ฃผ์ฐจ๋ณ„ ๋ฆฌ์ŠคํŠธ๋ทฐ์— ์ฒ˜์Œ ๋“ค์–ด ์™”์„ ๋•Œ, ์ปค๋ฆฌํ˜๋Ÿผ๋ทฐ์—์„œ ์œ ์ €๊ฐ€ ์„ ํƒํ•œ ์ฃผ์ฐจ์˜ item์œผ๋กœ ๋ฐ”๋กœ ๋„˜์–ด๊ฐ€์•ผ๋˜๊ธฐ ๋•Œ๋ฌธ์— scrollToItem์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ”ฅย Trouble Shooting

  1. ๊ธฐ๊ธฐ ๋Œ€์‘์„ ์œ„ํ•ด snapkit์„ ์‚ฌ์šฉํ•  ๋•Œ, multipliedBy ๋ฌธ์ œ

์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ์— ๋Œ€ํ•œ ๊ธฐ๊ธฐ ๋Œ€์‘์„ ์œ„ํ•ด ๊ฐ€๋กœ,์„ธ๋กœ ๋น„์œจ์„ ๊ณ„์‚ฐํ•  ๋•Œ multipliedBy์•ˆ์—์„œ ์—ฐ์‚ฐ์ด ๋˜์ง€ ์•Š์•„์„œ ๋”ฐ๋กœ ๋นผ์ฃผ์–ด์„œ ๊ณ„์‚ฐํ•ด์คฌ์Šต๋‹ˆ๋‹ค.

private enum Size {
        static let contentImageView: CGFloat = 120 / 335
    }

contentImageView.snp.makeConstraints{
            $0.height.equalTo(contentImageView.snp.width).multipliedBy(Size.contentImageView)
        }

  1. ์ปค๋ฆฌํ˜๋Ÿผ๋ทฐ Expandable Cell
func toggleButtonTapped(indexPath: IndexPath?) {
        self.isFirstPresented = false
        guard let indexPath  else { return }
        
        let previousWeekDatas = curriculumViewDatas[indexPath.section].weekDatas[indexPath.row]
        
        curriculumViewDatas[indexPath.section].weekDatas[indexPath.row].isExpanded = !previousWeekDatas.isExpanded
        curriculumTableView.reloadRows(at: [indexPath], with: .automatic)
    }

๋ฒ„ํŠผ์ด cell์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— delegate๋กœ ๋นผ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  cell์„ ๋งŒ๋“ค ๋•Œ, indexPath๋ฅผ ๋„˜๊ฒจ์ฃผ๊ณ , ํ† ๊ธ€๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ, ๊ทธ ํ•ด๋‹น ๋ฒ„ํŠผ์ด ์žˆ๋Š” indexPAth๋ฅผ ๊ฐ™์ด ๋„˜๊ฒจ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ expand๋œ row๋ฅผ ๋ฆฌ๋กœ๋“œํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

  1. scrollToRow ๊ตฌํ˜„
func scrollToUserWeek() {
        
        guard let userInfoData else { return }
        
        let userWeek = userInfoData.userWeekInfo
        
        if userWeek == 40 {
            let weekPerMonth = 4
            let desireSection = (userWeek / weekPerMonth) - 2
            let desireRow = (userWeek % weekPerMonth)
            let indexPath = IndexPath(row: desireRow, section: desireSection)
            
            curriculumViewDatas[desireSection].weekDatas[desireRow+4].isExpanded = true
            self.curriculumTableView.reloadData()
            self.curriculumTableView.scrollToRow(at: indexPath, at: .top, animated: false)
        } else {
            let weekPerMonth = 4
            let desireSection = (userWeek / weekPerMonth) - 1
            let desireRow = (userWeek % weekPerMonth)
            let indexPath = IndexPath(row: desireRow, section: desireSection)
            
            curriculumViewDatas[desireSection].weekDatas[desireRow].isExpanded = true
            self.curriculumTableView.reloadData()
            self.curriculumTableView.scrollToRow(at: indexPath, at: .top, animated: false)
        }
    }

๊ณ„์‚ฐ๋œ ์œ ์ €์˜ ์ฃผ์ฐจ๋กœ ์–ด๋Š row๋กœ ๊ฐ€์•ผ๋  ์ง€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์šฐ์„  ๋งค๋‹ฌ 4๊ฐœ์˜ ์ฃผ๊ฐ€ ์žˆ์–ด์„œ weekPerMonth๋ฅผ 4๋กœ ๊ณ ์ •ํ–ˆ๊ณ , desireSection์€ ๋ฐ›์•„์˜จ ์œ ์ €์˜ ์ฃผ์ฐจ / 4๋ฅผ ํ•œ ํ›„ -1์„ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Row๋Š” ๋‚˜๋ˆˆ ๊ฐ’์˜ ๋‚˜๋จธ์ง€๋กœ ๊ฐ€๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทผ๋ฐ 10๊ฐœ์›” section์—๋Š” 5๊ฐœ์˜ row๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰ ์ฃผ์ฐจ๊ฐ€ 5๊ฐœ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ 40์ผ๋•Œ๋งŒ ๋”ฐ๋กœ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฉ”์„œ๋“ค viewDidLoad์—์„œ ์‹คํ–‰์‹œ์ผฐ์„ ๋•Œ, ์ œ๋Œ€๋กœ scrollToRow๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ API๋กœ ๋ฐ›์•„์˜ค๋‹ˆ scrollToRow๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ›์•„์™€์ง€๊ธฐ ์ „(๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š”๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ)์ด๊ธฐ ๋•Œ๋ฌธ์— viewDidLoad์—์„œ ์‹คํ–‰์ด ์ œ๋Œ€๋กœ ๋˜์ง€ ์•Š์•˜๋˜ ๊ฒƒ์ด์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ viewDidLayoutSubviews์—์„œ scrollToRow ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.