Swift

CollectionView

khon98 2021. 3. 6. 10:22

https://github.com/khon98/CollectionView_BountyList

 

BountyList_CollectionView

더보기

CocoaTouchClass

import UIKit

class BountyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
    
    let viewModel = BountyViewModel()
    let bountyList = [30000000, 50, 5000000, 500000000, 10000000, 15000000, 200000000, 350000000]
    
    // segue를 수행하기 직전에 수행하는 것에 대해서 준비하는 메서드
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail" {
            
            let vc = segue.destination as? DetailViewController
            if let index = sender as? Int {
                
                let bountyInfo = viewModel.bountyInfo(at: index)
                vc?.viewModel.update(model: bountyInfo)
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // UICollectionViewDataSource
    // 셀을 몇개를 보여줄지
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return viewModel.numOfBountyInfoList
    }
    
    // 어떻게 표현할지
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GridCell", for: indexPath) as? GridCell else {
            return UICollectionViewCell()
        }
        
        let bountyInfo = viewModel.bountyInfo(at: indexPath.item)
            cell.update(info: bountyInfo)
        cell.update(info: bountyInfo)
        return cell
    }
    
    // UICollectionViewDelegate
    // 셀이 클릭 됐을 때
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("\(indexPath.item)")
        performSegue(withIdentifier: "showDetail", sender: indexPath.item)
    }
    
    // UICollectionViewDelegateFlowLayout
    // 셀 사이즈 계산 / 다른 디바이스에서도 동일하게 나오기 위한 작업
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        // 높이와 간격 지정
        let itemSpacing: CGFloat = 10 // 아이템 간격
        let textAreaHeight: CGFloat = 65 // 텍스트 간격
        
        let width: CGFloat = (collectionView.bounds.width - itemSpacing)/2
        let height : CGFloat = width * 10/7 + textAreaHeight
        return CGSize(width: width, height: height)
    }
 
}

// custom cell 생성
class GridCell: UICollectionViewCell {
    @IBOutlet weak var imgView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var bountyLabel: UILabel!
    
    func update(info: BountyInfo) {
        imgView.image = info.image
        nameLabel.text = info.name
        bountyLabel.text = "\(info.bounty)"
    }
}

// ViewModel
class BountyViewModel {
    let bountyInfoList: [BountyInfo] = [
        BountyInfo(name: "brook", bounty: 30000000),
        BountyInfo(name: "chopper", bounty: 50),
        BountyInfo(name: "franky", bounty: 5000000),
        BountyInfo(name: "luffy", bounty: 500000000),
        BountyInfo(name: "nami", bounty: 10000000),
        BountyInfo(name: "robin", bounty: 15000000),
        BountyInfo(name: "sanji", bounty: 200000000),
        BountyInfo(name: "zoro", bounty: 350000000)
    ]
    
    // 순위 정하기
    var sortedList: [BountyInfo] {
        let sortedList = bountyInfoList.sorted { prev, next in
            return prev.bounty > next.bounty
        }
        return sortedList
    }
    
    var numOfBountyInfoList: Int {
        return bountyInfoList.count
    }
    
    func bountyInfo(at index: Int) -> BountyInfo {
        return sortedList[index]
    }
}

 

 

 

CocoaTouchClass

import UIKit
class DetailViewController: UIViewController {
    
    @IBOutlet weak var imgView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var bountyLabel: UILabel!
    
    @IBOutlet weak var NameLabelCenterX: NSLayoutConstraint!
    @IBOutlet weak var BountyLabelCenterX: NSLayoutConstraint!
    
    let viewModel = DetailViewMomdel()
    
    // 화면이 보이기 직전
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
        
        // 애니메이션 준비하는 과정
        prepareAnimation()
    }
    
    // 애니메이션
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        showAnimation()
    }
    
    // 애니메이션 들어가기 전 기본 세팅
    private func prepareAnimation() {
        // view 밖으로 내보내기
//        NameLabelCenterX.constant = view.bounds.width
//        BountyLabelCenterX.constant = view.bounds.width
        
        nameLabel.transform = CGAffineTransform(translationX: view.bounds.width, y: 0).scaledBy(x: 3, y: 3).rotated(by: 180)
        bountyLabel.transform = CGAffineTransform(translationX: view.bounds.width, y: 0).scaledBy(x: 3, y: 3).rotated(by: 180)
        
        nameLabel.alpha = 0
        bountyLabel.alpha = 0
    }
    
    private func showAnimation() {
//        // 밖으로 내보내진 값들을 원위치
//        NameLabelCenterX.constant = 0
//        BountyLabelCenterX.constant = 0
        
        // label 애니메이션 효과 추가
        // namelabel 애니메이션
        UIView.animate(withDuration: 0.7, delay: 0.5, usingSpringWithDamping: 0.6, initialSpringVelocity: 2, options: .allowUserInteraction, animations: {
            
            // affinetransform은 변형을 가하는건데 변형을 가하기 전에 모습은 identity로 사용
            self.nameLabel.transform = CGAffineTransform.identity
            self.nameLabel.alpha = 1
        }, completion: nil)
        
        // bountylabel 애니메이션
        UIView.animate(withDuration: 0.7, delay: 0.7, usingSpringWithDamping: 0.5, initialSpringVelocity: 2, options: .allowUserInteraction, animations: {
            
            self.bountyLabel.transform = CGAffineTransform.identity
            self.bountyLabel.alpha = 1
        
        }, completion: nil)
        
        // 이미지 애니메이션 효과 추가
        UIView.transition(with: imgView, duration: 0.4, options: .transitionFlipFromLeft, animations: nil, completion: nil)
    }
    
    func updateUI() {
        if let bountyInfo = viewModel.bountyInfo {
            imgView.image = bountyInfo.image
            nameLabel.text = bountyInfo.name
            bountyLabel.text = "\(bountyInfo.bounty)"
        }
    }
    
    // close 버튼을 누르면 창이 닫힘
    @IBAction func close(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
}

class DetailViewMomdel {
    var bountyInfo: BountyInfo?
    func update(model: BountyInfo?) {
        bountyInfo = model 
    }
}