24.07.04 Today I Learned

2024. 7. 4. 21:06
// Model.swift

import Foundation

struct CoffeeList{
    
  let imageName: String
  let menuName: String
  let menuPrice: Int
}

extension CoffeeList{
    
  static let recommended_Menu = [
    CoffeeList(imageName: "plain_PongCrush", menuName: "플레인퐁크러쉬", menuPrice: 4500),
    CoffeeList(imageName: "iced_Cue_Brat", menuName: "아이스큐브라떼", menuPrice: 5200),
    CoffeeList(imageName: "grapefruit_Ade", menuName: "자몽에이드", menuPrice: 4800),
    CoffeeList(imageName: "iced_Apple_and_Citrus_Tea", menuName: "아이스사과유자차", menuPrice: 5000),
    CoffeeList(imageName: "strawberry_Latte", menuName: "딸기라떼", menuPrice: 5300),
    CoffeeList(imageName: "potato_Bread", menuName: "감자빵", menuPrice: 4200)
  ]
    
  static let smoothie_Menu = [
    CoffeeList(imageName: "golden_Mango_Smoothie", menuName: "골드망고스무디", menuPrice: 5800),
    CoffeeList(imageName: "strawberry_Yogurt_Smoothie", menuName: "딸기요거트스무디", menuPrice: 5500),
    CoffeeList(imageName: "strawberry_Cookie_Frappe", menuName: "딸기쿠키프라페", menuPrice: 5700),
    CoffeeList(imageName: "cookie_Frappe", menuName: "쿠키프라페", menuPrice: 5600),
    CoffeeList(imageName: "coconut_Coffee_Smoothie", menuName: "코코넛커피스무디", menuPrice: 5900),
    CoffeeList(imageName: "mint_Frappe", menuName: "민트프라페", menuPrice: 5400)
  ]
    
  static let coffee_Menu = [
    CoffeeList(imageName: "iced_Vanilla_Latte", menuName: "아이스바닐라라떼", menuPrice: 4900),
    CoffeeList(imageName: "iced_Americano", menuName: "아이스아메리카노", menuPrice: 4000),
    CoffeeList(imageName: "iscafemoca", menuName: "아이스카페모카", menuPrice: 5300),
    CoffeeList(imageName: "big_Hal_Mega_Coffee", menuName: "왕할메가커피", menuPrice: 6000),
    CoffeeList(imageName: "cold_Brew_Latte", menuName: "콜드브루라떼", menuPrice: 5100),
    CoffeeList(imageName: "iscara_melmakiatto", menuName: "아이스카라멜마끼아또", menuPrice: 5500)
  ]
    
  static let dessert_Menu = [
    CoffeeList(imageName: "honey_bread", menuName: "허니브레드", menuPrice: 7000),
    CoffeeList(imageName: "hot_Chicken_Deep_Cheese_Ciabatta", menuName: "핫 치킨&딥치즈 치아바타", menuPrice: 6500),
    CoffeeList(imageName: "cheesecake", menuName: "치즈케익", menuPrice: 5800),
    CoffeeList(imageName: "choco_Gelato_Croiffle", menuName: "초코젤라또크로플", menuPrice: 7200),
    CoffeeList(imageName: "iced_Honey_Waansu", menuName: "아이스허니와앙슈", menuPrice: 5000),
    CoffeeList(imageName: "butter_Egg_Bacon_Sandwich", menuName: "버터버터에그베이컨샌드위치", menuPrice: 6900)
  ]
    
  static let beverage_Menu = [
    CoffeeList(imageName: "cherry_Coke", menuName: "체리콕", menuPrice: 4500),
    CoffeeList(imageName: "ice_Choco", menuName: "아이스초코", menuPrice: 4700),
    CoffeeList(imageName: "ice_Grain_Latte", menuName: "아이스곡물라떼", menuPrice: 4800),
    CoffeeList(imageName: "peach_Ice_Tea", menuName: "복숭아아이스티", menuPrice: 4500),
    CoffeeList(imageName: "shine_Musket_Green", menuName: "샤인머스캣그린주스", menuPrice: 5200),
    CoffeeList(imageName: "strawberry_Banana", menuName: "딸기바나나주스", menuPrice: 5300)
  ]
    
  static let do_not_eat_Menu = [
    CoffeeList(imageName: "wm_Juice", menuName: "수박주스", menuPrice: 5000),
    CoffeeList(imageName: "bsbubblemt_Icedlatte", menuName: "흑당버블밀크티아이스라떼", menuPrice: 5500),
    CoffeeList(imageName: "bs_Icedbubblelatte", menuName: "흑당아이스버블라떼", menuPrice: 5500),
    CoffeeList(imageName: "cucumber_Limemojito", menuName: "오이오이라임오히또", menuPrice: 4800),
    CoffeeList(imageName: "grapefruit_honey_bt_Iced", menuName: "아이스허니자몽블랙티", menuPrice: 4900),
    CoffeeList(imageName: "ggultip_Option", menuName: "꿀팁옵션", menuPrice: 3800)
  ]
    
  static let tea_Menu = [
    CoffeeList(imageName: "chamomile_Tea", menuName: "캐모마일티", menuPrice: 5000),
    CoffeeList(imageName: "citron_Tea", menuName: "유자차", menuPrice: 5500),
    CoffeeList(imageName: "earlGrey_Tea", menuName: "얼그레이티", menuPrice: 5500),
    CoffeeList(imageName: "green_Tea", menuName: "녹차", menuPrice: 4800),
    CoffeeList(imageName: "honey_Grapefruit_BlackTea", menuName: "허니자몽블랙티", menuPrice: 4900),
    CoffeeList(imageName: "", menuName: "로얄밀크티", menuPrice: 5000)
  ]
    
//  static let pong_crush_Menu = [
//    CoffeeList(imageName: "choco_Honey_PongCrush", menuName: "초코허니퐁크러쉬", menuPrice: 5300),
//    CoffeeList(imageName: "banana_PongCrush", menuName: "바나나퐁크러쉬", menuPrice: 5200),
//    CoffeeList(imageName: "strawberry_PongCrush", menuName: "딸기퐁크러쉬", menuPrice: 5400),
//    CoffeeList(imageName: "plain_PongCrush", menuName: "플레인퐁크러쉬", menuPrice: 4500),
//    CoffeeList(imageName: "custardCream_Honey_PongCrush", menuName: "슈크림허니퐁크러쉬", menuPrice: 5600)
//  ]
    
}
// Category.swift

import UIKit
import SnapKit

protocol CoffeeListViewDelegate: AnyObject {
    func segmentValueChanged(to index: Int)
    func configureCollectionViewConstraints()
    func configureCollectionViewAppearance()
}

class CategoryView: UIView {
    
    weak var delegate: CoffeeListViewDelegate?
    
    let categories = ["추천메뉴", "커피", "디저트", "스무디", "티", "비추천메뉴"]
    
    var drinks: [[CoffeeList]] = [] // 컬렉션 뷰 데이터를 담을 배열
    
    lazy var segmentControl: UISegmentedControl = {
        let control = UISegmentedControl(items: categories)
        control.selectedSegmentIndex = 0
        control.addTarget(self, action: #selector(segmentValueChanged(_:)), for: .valueChanged)
        control.translatesAutoresizingMaskIntoConstraints = false
        control.backgroundColor = .white
        control.tintColor = .white
        control.selectedSegmentTintColor = .clear
        control.apportionsSegmentWidthsByContent = true // 텍스트 길이에 맞게 너비 조절
        
        let normalTextAttributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.systemFont(ofSize: 14) // 글자 크기를 축소하여 모든 텍스트가 보이도록 설정
        ]
        control.setTitleTextAttributes(normalTextAttributes, for: .normal)
        
        let selectedTextAttributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.boldSystemFont(ofSize: 14) // 선택된 상태의 글자 크기도 축소
        ]
        control.setTitleTextAttributes(selectedTextAttributes, for: .selected)
        
        return control
    }()
    
    lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = 10
        
        let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
        view.backgroundColor = .white
        view.delegate = self
        view.dataSource = self
        view.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    lazy var logoImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.image = UIImage(named: "logo")
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()
    

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupViews() {
        setupLogo()
        setupSegmentControl()
        setupCollectionView()
        
        drinks = [CoffeeList.recommended_Menu, CoffeeList.coffee_Menu, CoffeeList.dessert_Menu, CoffeeList.smoothie_Menu, CoffeeList.tea_Menu, CoffeeList.do_not_eat_Menu]
        
        collectionView.reloadData()
    }
    
    private func setupLogo() {
        addSubview(logoImageView)
        logoImageView.snp.makeConstraints { make in
            make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(0)
            make.centerX.equalToSuperview()
            make.width.equalTo(200)
            make.height.equalTo(100)
        }
    }
    
    private func setupSegmentControl() {
        addSubview(segmentControl)
        segmentControl.snp.makeConstraints { make in
            make.top.equalTo(logoImageView.snp.bottom).offset(20)
            make.left.right.equalToSuperview().inset(20)
        }
    }
    
    private func setupCollectionView() {
        addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.top.equalTo(segmentControl.snp.bottom).offset(20)
            make.left.right.equalToSuperview().inset(20)
            make.height.equalTo(300)
        }
        
        
        
        delegate?.configureCollectionViewConstraints()
    }
    
    @objc private func segmentValueChanged(_ sender: UISegmentedControl) {
        delegate?.segmentValueChanged(to: sender.selectedSegmentIndex)
    }
}

extension CategoryView: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return drinks[segmentControl.selectedSegmentIndex].count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        
        for subview in cell.contentView.subviews {
            subview.removeFromSuperview()
        }
        
        let coffee = drinks[segmentControl.selectedSegmentIndex][indexPath.item]
        
        let imageView = UIImageView()
        imageView.image = UIImage(named: coffee.imageName)
        imageView.contentMode = .scaleAspectFit
        cell.contentView.addSubview(imageView)
        imageView.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(5)
        }
        
        let label = UILabel()
        label.text = "\(coffee.menuName) - \(coffee.menuPrice)원"
        label.textAlignment = .center
        label.backgroundColor = .white
        label.clipsToBounds = true
        label.numberOfLines = 0
        label.lineBreakMode = .byTruncatingTail // Truncate tail for long text
        label.font = UIFont.systemFont(ofSize: 14) // 글자 크기 축소
        cell.contentView.addSubview(label)
        label.snp.makeConstraints { make in
            make.left.right.bottom.equalToSuperview().inset(5)
            make.top.equalTo(imageView.snp.bottom).offset(5)
        }
        
        return cell
    }
}

extension CategoryView: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 100, height: 150)
    }
}

 

// ViewController.swift

import UIKit
import SnapKit

class ViewController: UIViewController {
    
    let coffeeListView = CategoryView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        
        setupCoffeeListView()
    }
    
    private func setupCoffeeListView() {
        coffeeListView.delegate = self
        view.addSubview(coffeeListView)
        coffeeListView.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20))
        }
    }
}

extension ViewController: CoffeeListViewDelegate {
    func segmentValueChanged(to index: Int) {
        coffeeListView.collectionView.reloadData()
    }
    
    func configureCollectionViewConstraints() {
        // Additional constraints can be added here if needed
    }
    
    func configureCollectionViewAppearance() {
        coffeeListView.collectionView.backgroundColor = .white
    }
}
=======

내 코드는 이렇게 MVC를 나눠서 작성했다.

 

위 사진은 컬렉션뷰 파트를 맡은 인원과 내 코드를 합쳐서 만든 뷰다.

 

// Category.swift


// 카테고리

import UIKit
import SnapKit

protocol CoffeeListViewDelegate: AnyObject {
    func segmentValueChanged(to index: Int)
}

class CategoryView: UIView {
    
    weak var delegate: CoffeeListViewDelegate?
    
    private let categories = ["추천메뉴", "커피", "디저트", "스무디", "티", "비추천메뉴"]
    
    // 컬렉션 뷰 데이터를 담을 배열
    var drinks: [[CoffeeList]] = []
    
    // 세그먼트 컨트롤 관련 세부설정
    lazy var segmentControl: UISegmentedControl = {
        let control = UISegmentedControl(items: categories)
        control.selectedSegmentIndex = 0
        control.addTarget(self, action: #selector(segmentValueChanged(_:)), for: .valueChanged)
        control.backgroundColor = .white
        control.tintColor = .white
        control.selectedSegmentTintColor = .clear
        control.apportionsSegmentWidthsByContent = true
        
        // 폰트 설정
        let normalTextAttributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.systemFont(ofSize: 14)
        ]
        control.setTitleTextAttributes(normalTextAttributes, for: .normal)
        
        let selectedTextAttributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.boldSystemFont(ofSize: 14)
        ]
        control.setTitleTextAttributes(selectedTextAttributes, for: .selected)
        
        return control
    }()
    
    // 로고 이미지 뷰 생성
    lazy var logoImageView: UIImageView = {
        let img = UIImageView()
        img.contentMode = .scaleAspectFit
        img.image = UIImage(named: "logo")
        return img
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 뷰 설정 함수
    private func setupViews() {
        addSubview(logoImageView)
//        addSubview(segmentControl)
        
        // 세그먼트 컨트롤 관련 카테고리
//        drinks = [CoffeeList.recommended_Menu, CoffeeList.coffee_Menu, CoffeeList.dessert_Menu, CoffeeList.smoothie_Menu, CoffeeList.tea_Menu, CoffeeList.do_not_eat_Menu]
        
//        setupLayout()
    }
    
//    private func setupLayout() {
//        logoImageView.snp.makeConstraints { make in
//            make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(20)
//            make.centerX.equalToSuperview()
//            make.width.equalTo(200)
//            make.height.equalTo(100)
//        }
//        
//        segmentControl.snp.makeConstraints { make in
//            make.top.equalTo(logoImageView.snp.bottom).offset(20)
//            make.left.right.equalToSuperview().inset(20)
//        }
//    }
    
    // 세그먼트 컨트롤 값 변경 시 호출되는 함수
    @objc private func segmentValueChanged(_ sender: UISegmentedControl) {
        delegate?.segmentValueChanged(to: sender.selectedSegmentIndex)
    }
}

 

위 코드에서 주석처리 된 부분은 C부분에서 SBMenuController.swift에서 컬렉션뷰를 구성할 때 겹치는 코드여서 주석처리했다.

 

// MenuView.swift


import UIKit
import SnapKit

class MenuView: UICollectionViewCell {
    
    // 이미지 뷰 생성
    let imgView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFit
        return iv
    }()
    
    // 레이블 뷰 생성
    let label: UILabel = {
        let lbl = UILabel()
        lbl.textAlignment = .center
        lbl.backgroundColor = .white
        lbl.clipsToBounds = true
        lbl.numberOfLines = 0
        lbl.lineBreakMode = .byTruncatingTail
        lbl.font = UIFont.systemFont(ofSize: 14)
        return lbl
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(imgView)
        contentView.addSubview(label)
        
        setupLayout()
    }
    
    required init?(coder: NSCoder) {
        fatalError("*T_T*")
    }
    
    // 레이아웃 설정
    private func setupLayout() {
        imgView.snp.makeConstraints { make in
            make.top.left.right.equalToSuperview().inset(5)
            make.height.equalTo(contentView.snp.height).multipliedBy(0.6)
        }
        
        label.snp.makeConstraints { make in
            make.top.equalTo(imgView.snp.bottom).offset(5)
            make.left.right.bottom.equalToSuperview().inset(5)
        }
    }
}

 

// SBMenuController.swift


import UIKit
import SnapKit

class SBMenuController: UIViewController {
    
    // 컬렉션 뷰 생성
    let collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.backgroundColor = .white
        return cv
    }()
    
    // 카테고리 종류 생성
    let categories = ["추천메뉴", "커피", "디저트", "스무디", "티", "비추천메뉴"]
    
    // 카테고리 메뉴 배열
    var drinks: [[CoffeeList]] = [CoffeeList.recommended_Menu, CoffeeList.coffee_Menu, CoffeeList.dessert_Menu, CoffeeList.smoothie_Menu, CoffeeList.tea_Menu, CoffeeList.do_not_eat_Menu]
    
    var currentCategoryIndex: Int = 0
    
    // 로고 이미지 설정
    lazy var logoImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.image = UIImage(named: "logo")
        return imageView
    }()
    
    // 세그먼트 컨트롤 생성
    lazy var segmentControl: UISegmentedControl = {
        let control = UISegmentedControl(items: categories)
        control.selectedSegmentIndex = 0
        control.addTarget(self, action: #selector(segmentValueChanged(_:)), for: .valueChanged)
        control.backgroundColor = .white
        control.tintColor = .white
        control.selectedSegmentTintColor = .clear
        control.apportionsSegmentWidthsByContent = true
        
        let normalTextAttributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.systemFont(ofSize: 14)
        ]
        control.setTitleTextAttributes(normalTextAttributes, for: .normal)
        
        let selectedTextAttributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.boldSystemFont(ofSize: 14)
        ]
        control.setTitleTextAttributes(selectedTextAttributes, for: .selected)
        
        return control
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        collectionView.dataSource = self
        collectionView.delegate = self
        
        setupLayout()
    }
    
    // 레이아웃 설정
    private func setupLayout() {
        [logoImageView, segmentControl, collectionView].forEach {
            view.addSubview($0)
        }
        
        logoImageView.snp.makeConstraints {
            $0.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(20)
            $0.centerX.equalToSuperview()
            $0.width.equalTo(200)
            $0.height.equalTo(100)
        }
        
        segmentControl.snp.makeConstraints {
            $0.top.equalTo(logoImageView.snp.bottom).offset(20)
            $0.left.right.equalToSuperview().inset(20)
        }
        
        collectionView.snp.makeConstraints {
            $0.top.equalTo(segmentControl.snp.bottom).offset(20)
            $0.leading.trailing.bottom.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 20, bottom: 20, right: 20))
        }
        collectionView.register(MenuView.self, forCellWithReuseIdentifier: "img")
    }
    
    @objc private func segmentValueChanged(_ sender: UISegmentedControl) {
        currentCategoryIndex = sender.selectedSegmentIndex
        collectionView.reloadData()
    }
}

extension SBMenuController: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return drinks[currentCategoryIndex].count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "img", for: indexPath) as! MenuView
        let menuItem = drinks[currentCategoryIndex][indexPath.item]
        cell.imgView.image = UIImage(named: menuItem.imageName)
        cell.label.text = "\(menuItem.menuName) - \(menuItem.menuPrice)원"
        return cell
    }
}

extension SBMenuController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 110, height: 130)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 80
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 5
    }
}

 

 

// ViewController.swift


import UIKit
import SnapKit
import SwiftUI

class ViewController: UIViewController {
    
    let menuController = SBMenuController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        
        setupMenuController()
    }
    
    private func setupMenuController() {
        addChild(menuController)
        view.addSubview(menuController.view)
        menuController.didMove(toParent: self)
        
        menuController.view.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
}

struct PreView123: PreviewProvider {
    static var previews: some View {
        ViewController().toPreview123()
    }
}

 

 

'Today I Learned > 2024' 카테고리의 다른 글

24.07.11 Today I Learned  (0) 2024.07.11
24.07.08 Today I Learned  (0) 2024.07.08
24.07.02 Today I Learned  (0) 2024.07.02
24.07.01 Today I Learned  (0) 2024.07.01
24.06.28 Today I Learned  (0) 2024.06.28

BELATED ARTICLES

more