iOS/Toy project

[iOS : Toy Project] Head Space Focus

yevdev 2022. 6. 7. 23:13

📌 아홉번째 프로젝트 

명상 컨텐츠 리스트 앱을 만들어보자

 

 

1️⃣ Data 확인 및 SearchViewController 만들기

- 이전 프로젝트들과 마찬가지로 패캠에서 제공해준 데이터들을 사용

- "FocusViewController" 이름의 UIViewController을 만들어서, Main storyboard와 연결까지 완료

 

 

2️⃣ CollectionView AutoLayout 설정

- Description과 ContentView와의 관계에서 bottom을 30 이상이 되게 하면

이렇게 빨간색 에러점이 뜨게 되는데, 일단 이건 어쩔 수 없으니까 넘어간다.

 

 

3️⃣ UICollectionViewCell 만들기

- "FocusCell" 이름의 UICollectionViewCell을 만들어서 CollectionViewCell 연결 및 Collection Reusable Identifier을 설정

//  FocusCell.swift

import UIKit

class FocusCell: UICollectionViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var thumbnailImageView: UIImageView!
    
    func configure(_ item: Focus){
        titleLabel.text = item.title
        descriptionLabel.text = item.description
        thumbnailImageView.image = UIImage(systemName: item.imageName)
    }
}

 

 

4️⃣ UIViewController 정리

- Diffable Datasource, Snapshot, Compositional Layout

//  FocusViewController.swift

import UIKit

class FocusViewController: UIViewController {
    
     @IBOutlet weak var collectionView: UICollectionView!
    
    var items: [Focus] = Focus.list
    
    typealias Item = Focus
    enum Section {
        case main
        // Section이 많아지면 여기에 case를 더 추가하면 됨.
    }
    
    var datasource: UICollectionViewDiffableDataSource<Section, Item>!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // (1) presentation -> DiffableDataSource
        datasource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in
            
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FocusCell", for: indexPath) as? FocusCell else {
                return nil
            }
            cell.configure(item)
            return cell
        })
        
        // (2) data -> Snapshot
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.main])
        snapshot.appendItems(items, toSection: .main)
        datasource.apply(snapshot)
        // snapshot을 설정해주면, 이 정보말고 나머지는 가짜라고 설정된다.
        
        // (3) laout -> Compositional layout
        collectionView.collectionViewLayout = layout()

    }
    
    private func layout() -> UICollectionViewCompositionalLayout{
        
        // .estimated(50) == 기본적으로 50인데 그저 추정값, 늘어남에 따라서 알아서 표현이 된다.
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
        let section = NSCollectionLayoutSection(group: group)
        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
        
    }
    

}

- Diffable Datasource를 쓰면,

이전의

let data = self.items[indexPath.item]

cell.configure(data)

cell.configure(item)

으로 간단하게 바꿀 수 있다.

 

지금까지 구현된 화면

 

 

5️⃣ 좀 더 꾸미기

- 기본 tint값이 입혀진 image를 multicolor로 바꿔보기

// FocusCell.swift
func configure(_ item: Focus){
        titleLabel.text = item.title
        descriptionLabel.text = item.description
        thumbnailImageView.image = UIImage(systemName: item.imageName)?.withRenderingMode(.alwaysOriginal)
}

withRenderingMode(.alwaysOriginal)

 

 

- 색과 Radius 설정

import UIKit

class FocusCell: UICollectionViewCell {
// ...
    
    override func awakeFromNib() {
        super.awakeFromNib()
        contentView.backgroundColor = UIColor.systemIndigo
        contentView.layer.cornerRadius = 10
    }
    
// ...
}

 

- 좌우 패딩, group간의 spacing 주기

//  FocusViewController.swift

import UIKit

class FocusViewController: UIViewController {
	
    // ...
    
    private func layout() -> UICollectionViewCompositionalLayout{
        
        // ...
        
        let section = NSCollectionLayoutSection(group: group)
        // section에 좌우패딩주기
        section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)
        // group간의 spacing
        section.interGroupSpacing = 10
        
        // ...
        
        return layout
        
    }
    
}

지금까지 구현된 화면

 

 

6️⃣ 버튼 만들기

- Auto Layout

Button의 AutoLayout

 

- 버튼 기능

//  FocusViewController.swift

import UIKit

class FocusViewController: UIViewController {
    
    @IBOutlet weak var refreshButton: UIButton!
    
    var currated: Bool = false
    
// ...

    override func viewDidLoad() {
    
        // ...
        
        // 삼항연산자를 통해 Button의 상태 설정
        let title = currated ? "See All" : "See Recommendation"
        refreshButton.setTitle(title, for: .normal )

    }
  
  	// ...
    
    @IBAction func refreshButtonTapped(_ sender: Any) {
        currated.toggle()
        
        self.items = currated ? Focus.recommendations : Focus.list
        
        // collectionView가 업데이트 되어야 함
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.main])
        snapshot.appendItems(items, toSection: .main)
        datasource.apply(snapshot)
        
        let title = currated ? "See All" : "See Recommendation"
        refreshButton.setTitle(title, for: .normal)
    }
    
}

 

 

🚀 구현 완료

 

 

 

7️⃣ Code Refactoring (DRY : Do not Repeat Yourself)

완성된 코드를 확인해보면

 let title = currated ? "See All" : "See Recommendation"

이 코드가 두번 반복되고 있는 것을 볼 수 있다.

→ 함수로 처리하기!

func updateButtonTitle() {

        let title = currated ? "See All" : "See Recommendation"

        refreshButton.setTitle(title, for: .normal)

    }

 

 


📌 정리,

  • Presentation → Diffable DataSource
  • Data → Snapshot
  • Layout → Compositional Layout
  • @IBAction 으로 Button 기능 구현하기

 

 


Reference

패스트캠퍼스 온라인 강의