[ProJect 일지] 키오스크 (1)

처음 팀프로젝트가 시작됐다. 기대했던 프로젝트 였기에 떨리고 긴장되는 마음이었는데 진행될 수록 팀원들 덕분에 착착 순조롭게 진행되는 것 같아 점점 더 재밌어지는 것 같다

이 프로젝트가 진행되는 일지를 적어보려고 한다.

우리팀은 키오스크 만들기 중 애플 제품을 판매하는 앱을 만드는 것을 방향으로 잡고 각자의 역할을 분담에 맡았다.

나는 그 중 세그먼트 컨트롤러를 이용해 각 세그별로 컬렉션 뷰로 제품을 나열하는 기능을 구현하는 것을 맡았다.


Model 생성

먼저 데이터 모델을 만들어주자

struct AppleProduct {
    let image: UIImage?
    let name: String
    let price: Int
    let category: String
}

내가 컬렉션 뷰에 보여줄 데이터와 그것을 분류해 줄 카테고리를 만들기 위해 이렇게 데이터 모델을 구성했다.

class DataManager {
    var products: [AppleProduct] = []
    
    init() {
        // 제품 데이터 초기화 및 추가
    }
}

그리고 데이터 매니저 클래스를 생성하여 컬렉션 뷰 안에 들어갈 데이터를 넣어주었다.


View 생성

제품을 표시할 컬렉션 뷰 셀을 ProductCell로 지정하고 컬렉션 뷰 내 들어갈 컬렉션 셀을 코드로 작성해 넣었다. 원래는 스토리보드로 들어가 있었지만 스토리보드 오토레이아웃이 계속 꼬이는 바람에 그냥 지우고 코드로 다시 작성하게 되었다.

먼저 데이터를 넣어줄 이미지와 레이블을 생성했다.

// 이미지 뷰
    let productImage: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()
    
    // 제품 이름 레이블
    let productLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.boldSystemFont(ofSize: 18)
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    // 제품 가격 레이블
    let productPrice: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 14)
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

그 다음 이 생성된 컴포넌트들을 오토레이아웃을 잡아주었다.

override init(frame: CGRect) {
    super.init(frame: frame)
    
    addSubview(productImage)
    addSubview(productLabel)
    addSubview(productPrice)
    
    NSLayoutConstraint.activate([
        productImage.topAnchor.constraint(equalTo: topAnchor),
        productImage.leadingAnchor.constraint(equalTo: leadingAnchor),
        productImage.trailingAnchor.constraint(equalTo: trailingAnchor),
        productImage.heightAnchor.constraint(equalToConstant: 150),
        productImage.widthAnchor.constraint(equalToConstant: 150),
        
        productLabel.topAnchor.constraint(equalTo: productImage.bottomAnchor, constant: 8),
        productLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
        productLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
        
        productPrice.topAnchor.constraint(equalTo: productLabel.bottomAnchor, constant: 4),
        productPrice.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
        productPrice.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
        productPrice.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
    ])
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

그 이후 제품 데이터를 만들어준 컴포넌트 내 넣어주는 식을 작성하였다.

func configure(with product: AppleProduct) {
    productImage.image = product.image
    productLabel.text = product.name
    // 가격 포맷팅 및 표시
}

이렇게 까지 해서 Model 과 View 의 구성을 마치게 되었고 그다음엔 컨트롤러를 설정해준다


Controller 생성

let dataManager = DataManager()
  let cellMarginSize: CGFloat = 2.0
  var filteredProducts: [AppleProduct] = [] {
      didSet {
          mainCollectionView.reloadData()
      }
  }

데이터를 컨트롤러에 가져와 컬렉션뷰의 변화가 있을 때마다 didSet 을 통해 데이터가 리로드 될 수 있도록 코드를 구현해주도록 한다.

mainCollectionView.dataSource = self
mainCollectionView.delegate = self

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return filteredProducts.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProductCell", for: indexPath) as! ProductCell
        let product = filteredProducts[indexPath.item]
        cell.configure(with: product)
        return cell
    }
}

그 이후 이전 테이블 뷰에서 했던 것과 마찬가지로 컬렉션뷰도 동일하게 델리게이트를 선언하고 데이터 소스를 가져와야한다.

지금 나온 두가지 필수 구현사항은 이전 테이블 뷰에서 많이 접했던 내용으로 익숙했다.

numberOfItemsInSection 은 셀의 갯수를 지정하는 것으로 filteredProducts 즉 세그먼트를 통해 필터링 된 해당하는 카테고리에 맞는 데이터의 갯수를 가져왔다.

cellForItemAt 은 각 셀을 생성하고 재사용 가능한 셀을 가져와 데이터 모델을 해당 셀과 연결하고 반환한다.

그 이후 이제 세그먼트 컨트롤러와 컬렉션 뷰의 데이터를 연결하여 필터링된 데이터를 확인할 수 있도록 구현해보자

@IBAction func segmentValueChanged(_ sender: UISegmentedControl) {
    switch sender.selectedSegmentIndex {
        case 0:
            filteredProducts = dataManager.products.filter { $0.category == "맥북" }
        case 1:
            filteredProducts = dataManager.products.filter { $0.category == "아이폰" }
        case 2:
            filteredProducts = dataManager.products.filter { $0.category == "패드" }
        case 3:
            filteredProducts = dataManager.products.filter { $0.category == "워치" }
        case 4:
            filteredProducts = dataManager.products.filter { $0.category == "악세사리" }
        default:
            break
    }
    mainCollectionView.reloadData()
}

먼저 미리 스토리보드에 구현된 세그먼트컨트롤러를 IBAction 으로 가져와 세그먼트의 인덱스 즉 각 카테고리별로 눌렸을때 데이터의 카테고리를 통해 필터링 된 값을 가질 수 있도록 코드를 구현했다.

지금 여기까지 과정을 순탄하게 진행할 수 있었으나, 그 이후 컬렉션뷰의 크기를 지정하는 것에 오류가 생겨 자꾸 데이터가 셀에 제대로 보이지 않아 애를 먹었다.

컬렉션 뷰 셀의 크기를 지정하고 그안에 세부적인 변동사항을 지정하고 싶을 땐

바로 컬렉션 뷰의 FlowLayout 을 설정해주어야 한다.

 func createFlowLayout() -> UICollectionViewFlowLayout {
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .vertical
    layout.itemSize = CGSize(width: 150, height: 100)
    layout.estimatedItemSize = CGSize(width: 160, height: 200)
    layout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
    return layout
}

createFlowLayout 메서드를 만들어 UICollectionViewFlowLayout 를 생성하고 설정한다.

수직으로 스크롤이 되도록 설정하고 각 셀의 크기를 설정했다.

그 이후 sectionInset 를 통해 각 셀의 내부 여백을 맞춰주니 다행히 원했던 뷰가 만들어졌다..

이 과정이 가장 어렵고 힘든 과정이었다 ㅠㅠ

이렇게 만들어진 createFlowLayout 를 적용할 수 있도록 뷰디드로드에 해당 값을 넣어준다.

let flowLayout = createFlowLayout()
mainCollectionView.collectionViewLayout = flowLayout

 

 

이렇게 완료하면 구현은 완료!

팀원들에게 많은 도움을 받아 생각보다 수월하게 진행할 수 있었다. 이렇게 진행된 내용을 기록하며 내가 부족한 점과 알게된 점을 기록하도록 하자 🙂

지금까지 진행된 내용을 다른 분들과 공유하여 머지를 한 지금까지의 결과이다.

추가적으로 넣을만한 기능을 생각해 적용해볼 생각이다.