ios 19일차

bin·2026년 1월 23일

과제에 관한 기록

구상

https://github.com/mastarTrack/HarryPotterBookTracker/tree/yeongbin
README.md 참고

구현 3

  • 구현 3의 요구사항은 책 정보 영역 아래의 Dedication과 Summary를 구성하는 것이다.

코드에서 알 수 있듯이, StackView를 2개 구성하여 Dedication, Summary에 관련된 속성을 묶어서 정의하여 표시하였다.

BookSummaryStackView.swift

import UIKit
import SnapKit

class BookSummaryStackView: UIStackView {
    
    let dedicationStackView = UIStackView()
    let dedicationLabel = UILabel()
    let dedicationValueLabel = UILabel()
    
    let summaryStackView = UIStackView()
    let summaryLabel = UILabel()
    let summaryValueLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setAttributes()
        setLayout()
    }
    
    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
}

private extension BookSummaryStackView {
    
    private func setAttributes() {
        
        // 메인 스택뷰 영역간 간격 24
        self.axis = .vertical
        self.spacing = 24
        self.alignment = .leading
        
        // 타이틀과 내용 사이 간격 8
        [dedicationStackView, summaryStackView].forEach {
            $0.axis = .vertical
            $0.spacing = 8
            $0.alignment = .leading
        }
        
        [dedicationLabel, summaryLabel].forEach {
            $0.font = .systemFont(ofSize: 18, weight: .bold)
            $0.textColor = .black
        }
        
        [dedicationValueLabel, summaryValueLabel].forEach {
            $0.font = .systemFont(ofSize: 14)
            $0.textColor = .darkGray
            $0.numberOfLines = 0
        }
        
        dedicationLabel.text = "Dedication"
        summaryLabel.text = "Summary"
    }
    
    private func setLayout() {
        
        // Label -> StackView
        [dedicationLabel, dedicationValueLabel].forEach { dedicationStackView.addArrangedSubview($0) }
        [summaryLabel, summaryValueLabel].forEach { summaryStackView.addArrangedSubview($0) }
        
        // StackView 합치기
        [dedicationStackView, summaryStackView].forEach { self.addArrangedSubview($0) }
    }
}

extension BookSummaryStackView {
    func configure(dedication: String, summary: String) {
        dedicationValueLabel.text  = dedication
        summaryValueLabel.text = summary
    }
}

구현 4

  • 구현 4의 요구사항은 크게 두 가지이다. 구현 3에서 구성한 Dedication, Summary 영역 아래에 Chapter 정보를 가져와서 표시하는 것, 스크롤 뷰를 적용하는 것이다.

BookInfo.swift

data.json을 파싱한 데이터를 저장하는 객체에 추가로 Chapter를 생성해준다. Chapter는 내부에 title이 존재하여 배열로 받아오도록 구성한다.

struct Chapter: Codable {
    let title: String
}

BookChapterStackView.swift

단순하게 생각하고 작업하기로 했다. 기존의 방식대로 StackView를 2개 생성하여 titleLabel과 chapterListStackView를 구성한다. chapterListStackView는 Chapter 배열 내부의 chapter만큼 뷰를 생성하여 chapterListStackView에 적용하여 생성한다.
기존의 방식처럼 여기서 끝내면 문제가 발생한다. 기존의 데이터들을 labe.text = book.title 이렇게 데이터만을 변경하는 것으로 끝이지만, chapterlistStackView는 새로운 뷰를 생성하여 쌓는 방식이므로 만약 데이터가 변경되면 기존의 Chapter에서 변경되는 것이 아니라 아래에 쌓이게 된다.
그러므로 기존의 뷰를 삭제하는 로직이 필요하여 추가하였다.

import UIKit
import SnapKit

class BookChapterStackView: UIStackView {
    
    private let titleLabel = UILabel()
    private let chapterListStackView = UIStackView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setAttribute()
        setLayout()
    }
    
    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension BookChapterStackView {

    func setAttribute() {
        self.axis = .vertical
        self.spacing = 8
        self.alignment = .leading
        
        titleLabel.text = "Chapters"
        titleLabel.textColor = .black
        titleLabel.font = .systemFont(ofSize: 18, weight: .bold)
        
        chapterListStackView.axis = .vertical
        chapterListStackView.spacing = 8 // 각 챕터 사이 간격 8
        chapterListStackView.alignment = .leading
    }
        
    func setLayout() {
        [titleLabel, chapterListStackView].forEach { addArrangedSubview($0) }
    }
}

extension BookChapterStackView {
    func config(with chapters: [Chapter]) {
        chapterListStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } // 뷰를 삭제하는 로직 필요 : 스택뷰는 아래에 계속 쌓임.(addArrangedSubView를 사용하기에 덮어쓰지 않음)
        
        
        // 새로운 뷰를 생성
        chapters.forEach { chapter in
            let label = UILabel()
            label.text = chapter.title
            label.textColor = .darkGray
            label.font = .systemFont(ofSize: 14)
            chapterListStackView.addArrangedSubview(label)
            
        }
    }
}

ViewController.swift

수정된 부분이 꽤 많아서 코드를 가져와서 보여주기가 더 힘들 것 같아서 글로 작성한다. 일단, ScrollView가 적용되는 위치를 먼저 잡도록 했다. ScrollView는 SeriesButton 아래부터 적용되어 bookChapterStackView까지 적용된다. 이 부분들을 scrollView로 묶어주고 스크롤 뷰 내부에 contentView를 추가해준다. 그리고 contentView 내부에 스크롤이 적용되어야 하는 bookInfowView, bookSummaryStackView, bookChapterStackView를 넣어준다. 속성에 관련된 내용은 기존에 정의된 속성들이 contentView에 적용되었다. 또한, 구현 요구사항으로 나온 스크롤 바 숨기기를 scrollView.showsVerticalScrollIndicator = false로 구현했다.

회고

UI와 관련된 작업을 본격적으로 시작하는 첫 주기에, 공부보다는 직접 만들어보는 시간이 필요했다. 과제를 구현하기에 앞서, 구글링을 통해 찾은 인스타그램 클론코딩으로 공부를 먼저 하고 시작했더니, 조금은 이해를 하면서 작업했던 것 같다. (물론, 아직 어느 상황에 어떤 것을 사용하는 것이 더 좋다, View들의 각각의 장단점을 잘 안다.라고는 얘기하지 못한다.) 과제를 진행하면서도 정리되지 않은 개념, 잘 모르는 개념들이 있었기에 검색을 통해 조금씩 문제를 풀어나갔다. 일단 한번 먼저 따라 작성해 본 내용들도 있고, 읽어보며 이해하고 본인만의 방법으로 이렇게 하는게 더 좋지 않나? 생각하여 적용도 해보았다. 일단 돌아가니까 됐다라는 생각보다 다른 사람의 생각을 이용하여 문제를 풀었다면 왜 그런지를 생각해보고 정리하는 시간을 가졌다. 한번에 문제 해결을 원만하게 할 수는 없다고 생각한다. 검색을 통해 지식을 얻고, 본인만의 학습을 통해 이를 정리하고 쌓아가다보면 더 이상 검색 없이도 문제 해결을 해낼 수 있을 것이라 생각한다.

0개의 댓글