리스트와 그리드는 필수다. 결국 메인이 될 페이지를 개발할때 사용하는 컴포넌트다.
크게 UITableView, UICollectionView 둘이 비슷하기도 하고 테이블이 콜렉션에 종속되어있는 느낌이라 콜렉션 뷰를 학습할 것이다.
콜렉션뷰로 리스트와 그리드를 개발할때는 3개만 고려하자. [데이터,프레젠테이션(셀 표현),레이아웃]
1.먼저 뷰안에 컬렉션뷰를 만들고 안의 내용을 꾸며준다.(생략)
2.그리고 컬렉션셀 파일을 만든다.
StockRankCollectionViewCell
//
// StockRankCollectionViewCell.swift
// stockmarket
//
// Created by 이은호 on 2023/02/18.
//
import UIKit
class StockRankCollectionViewCell: UICollectionViewCell {
}
3.뷰컨->컬렉션뷰->컬렉션뷰셀의 클래스와 파일을 연결하고, identifier도 동일한 이름으로 작성한다.
4.어시스턴트 코드
//
// StockMarketViewController.swift
// stockmarket
//
// Created by 이은호 on 2023/02/18.
//
import UIKit
class StockMarketViewController: UIViewController {
let stockList: [StockModel] = StockModel.list
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
}
}
extension StockMarketViewController: UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return stockList.count
}//리스트의 크기
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StockRankCollectionViewCell", for: indexPath)
return cell
}//indentifier
}
extension StockMarketViewController: UICollectionViewDelegate{
}
배치는 되었지만 모양이 이상하다. 고쳐보도록 하자.
바로 여기서 비어있는 UICollectionViewDelegate를 핸들링하게 된다.
이슈가 하나 있는데 생성되는것은 UICollectionViewDelegated이지만 FlowLayout를 사용해야한다!
extension StockMarketViewController: UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.width, height: 80)
}
}
이 작업은 뷰컨파일이 아닌 뷰컨셀파일에서 작업한다.
import UIKit
class StockRankCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var rankLabel: UILabel!
@IBOutlet weak var companyName: UILabel!
@IBOutlet weak var priceLabel: UILabel!
@IBOutlet weak var companyLogoImage: UIImageView!
@IBOutlet weak var diffLabel: UILabel!
func configure(_ stock:StockModel){
rankLabel.text = "\(stock.rank)"
companyName.text = "\(stock.name)"
priceLabel.text = "\(stock.price)"
diffLabel.text = "\(stock.diff) 원"
companyLogoImage.image = UIImage(named: stock.imageName)
}
}
이경우에는 아무것도 안바뀌는데 상위 클래스와 통신이 안되었기때문이다.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StockRankCollectionViewCell", for: indexPath) as? StockRankCollectionViewCell else{
return UICollectionViewCell()
}
print(indexPath.item)
let stock = stockList[indexPath.item]
cell.configure(stock)
return cell
}//indentifier
guard에 대해서는 부록에서 복습해보도록 하자
diff가 음수이면 파란색으로 디자인해주자.
func configure(_ stock:StockModel){
rankLabel.text = "\(stock.rank)"
companyName.text = "\(stock.name)"
priceLabel.text = "\(convertCurrency(price:stock.price))원"
diffLabel.text = "\(stock.diff)%"
companyLogoImage.image = UIImage(named: stock.imageName)
if stock.diff < 0 {
diffLabel.textColor = UIColor.blue
}
}
diffLabel.textColor = stock.diff < 0 ? UIColor.systemBlue : UIColor.systemRed
밑에처럼 표현도 가능하다.
디자인(생략) 완벽한 반복이다.
다른 부분이 있다면
3.날짜형식도 월-일만 나오게 하자.
func FormmaterDate(dateString:String)->String{
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
if let date = formatter.date(from: dateString) {
formatter.dateFormat = "M/d"
return formatter.string(from: date)
}else{
return ""
}
}
날짜를 내림차순으로 정렬해보자
chatList = chatList.sorted(by: { chat1, chat2 in
return chat1.date > chat2.date
})
이미지의 테두리를 깎아보자
override func awakeFromNib() {
super.awakeFromNib()
userImage.layer.cornerRadius = 20
}
정말 간단하다. 레이아웃만 만져주면 된다.
extension AppViewController: UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (collectionView.bounds.width - 20) / 3
let height = width * 1.5
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
}
스토리보드의 상단바를 클릭하고 editor -> embed in -> navigation controller 클릭
타이틀을 스토리보드에서 클릭해서 수정해주면 된다.
콜렉션뷰를 세이프에이리어 밖까지 지정해주고 constant까지 0으로 설정해주자. 네비게이션에 블러처리가 된다.
인셋이 뭐냐하면 마진이다 ㅋㅋㅋ
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.contentInset = UIEdgeInsets(top: 20, left: 10, bottom: 0, right: 10)
}
이제 요소들을 클릭했을때 정보를 출력할 수 있도록 해보자.
extension AppViewController: UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let app = list[indexPath.item]
print("\(app.name)")
print("\(app.description)")
}
}
4행으로 만들면 글자가 잘린다.
guard문은 조건이 틀린 경우는 모두 버리고, 우리가 원하는 조건만 통과시키겠다는 기능으로 사용된다. if문과의 차이점으로 보면 if문은 '~면 ~해라' 의 실행 구문이지만, guard는 '~아니면 끝내라' 라는 의미이기 때문이다. 따라서 guard문은 '빠른 종료'의 핵심이라고 한다.
guard구문은 함수나 메서드 혹은 반복문 안에서만 사용할 수 있다는 한계점이 있다. 쉽게 말해 {}안에서만 사용한다는 것이다.
사실 if문보다 특별히 잘난점도 못난점도 없지만 if else가 {}을 두번쓰는 반면, {}을 한 번 쓰기 때문에 가독성이 조금 좋아진다고 보면 되겠다.
...조금 잘난점이 있다.
파라미터에 _을 선언하게 되면, 해당 파라미터의 레이블을 생략할 수 있게 된다.
func solution(str:String) {
print(str)
}
solution(str:"사랑해")
func solution(_ str:String) {
print(str)
}
solution("사랑해")