내가 만들어 볼 예제는 강아지들을 표현하는 아주 간단한 예제이다. 왜냐고? 내가 강아지를 좋아하기 때문이지.. 🐶
바로 시작 해보자!
0. 더미 데이터 만들기
API를 사용하거나 외부에서 받아오는 것이 아니기 때문에 내가 대충 강아지 정보를 더미 데이터로 만들었다.
1. 스토리보드로 컬렉션 뷰 구성하기
표현하고자 하는 내용(강아지 사진, 이름, 견종, 설명)을 위해 이렇게 구성해봤다. 물론 오토 레이아웃도 적용했다. 별로 어려운 부분이 아니니 빠르게 다음 단계로 가자 😎
2. ViewController에 CollectionView 연결하기
화면 구성을 마무리 했으면 ViewController에 연결해야한다. 전에 공부했던 IBO를 이용해서 연결해주면 된다 !
import UIKit
class puppyViewController: UIViewController {
@IBOutlet weak var puppyCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
나는 컬렉션 뷰의 이름을 구분하기 쉽게 puppyCollectionView로 했다. 그냥 collectionView로 해도 되지만 나중에 컬렉션 뷰를 많이 쓸 상황을 생각해서 미리 습관을 들이려고 한다.
3. CollectionView를 구성할 cell 만들기
나는 cell을 위한 클래스를 따로 만들어서 관리 할 것이기 때문에 아래와 같이 puppyCell 파일을 따로 만들어 주었다. 개인적으로 이렇게 나눠서 만드는? 습관을 들이고 있다. 여기저기에 섞여있으면 관리하기 힘들 것 같아서 그렇다.
자 그럼 이렇게 만든 친구를 셀에 연결해줘야겠지? 이렇게 연결하고자 하는 셀에 클래스 이름을 입력해주면 된다. 그리고 가장 중요한 Collection Reusable View 여기에 식별자로도 입력 해줘야한다. 보통은 클래스명과 동일하게 하는게 관행이라고 한다.
자 그럼 셀을 만들어볼까? 셀을 만들기 위해서는 해당 셀에 표현할 녀석들을 셀에 연결해주어야 한다. 똑같이 IBO로 연결해주면 된다 !
import UIKit
class puppyCell: UICollectionViewCell {
@IBOutlet weak var puppyImageView: UIImageView!
@IBOutlet weak var puppyNameLabel: UILabel!
@IBOutlet weak var puppyBreedLabel: UILabel!
@IBOutlet weak var puppyInfoLabel: UILabel!
}
연결을 끝냈으면,, 해당 객체에 데이터를 전달할 함수를 만들어준다. 함수의 인자로 Puppy 타입의 puppylList를 받아오는데, Puppy는 내가 만든 더미 데이터이다. (구조체로 리스트 만듦)
import UIKit
class puppyCell: UICollectionViewCell {
@IBOutlet weak var puppyImageView: UIImageView!
@IBOutlet weak var puppyNameLabel: UILabel!
@IBOutlet weak var puppyBreedLabel: UILabel!
@IBOutlet weak var puppyInfoLabel: UILabel!
func printPuppy(_ puppyList: Puppy) {
puppyImageView.image = UIImage(named: puppyList.imageName)
puppyNameLabel.text = "\(puppyList.name)"
puppyBreedLabel.text = "(\(puppyList.dog))"
puppyInfoLabel.text = "\(puppyList.puppyInfo)"
}
}
4. ViewController에서 CollectionView 표현하기
import UIKit
class puppyViewController: UIViewController {
let list:[Puppy] = Puppy.list
@IBOutlet weak var puppyCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
puppyCollectionView.dataSource = self
puppyCollectionView.delegate = self
}
}
extension puppyViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return list.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = puppyCollectionView.dequeueReusableCell(withReuseIdentifier: "puppyCell", for: indexPath) as? puppyCell else {
return UICollectionViewCell()
}
let list = list[indexPath.item]
cell.printPuppy(list)
return cell
}
}
extension puppyViewController:UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: puppyCollectionView.bounds.width, height: 200)
}
}
컬렉션 뷰에게 dataSource와 delegate를 제공해야한다. dataSource는 UICollectionViewDataSource를 통해, delegate는 UICollectionViewDelegate를 통해 전달할 수 있다.
일단 UICollectionViewDataSource는 필수 요소 두가지가 있는데, numberOfItemsInSection과 cellForItemAt이다.
numberOfItemsInSection는 간단히 말하면 셀을 얼만큼 만들껀데? 라고 생각하면 쉽다. 나는 내 더미데이터 만큼만 만들고 싶어서 반환 값을 리스트의 개수로 했다.
cellForItemAt는 셀을 어떻게 구성할껀데? 라고 생각하면 쉽다. 나는 dequeueReusableCell를 이용해서 셀을 재사용 했다. 셀의 재사용은 진짜 중요한 개념인 것 같아서 나중에 정리해서 포스팅 할 생각이다. 아무튼 cell은 옵셔널 값이라서 옵셔널 언래핑을 해줘야한다. 나는 guard를 이용해서 언래핑 했다 ! 그리고 아까 작성한 printPuppy 함수를 사용해서 cell을 만들고 리턴 해주었다.
마지막으로 셀의 크기를 지정하기 위해 UICollectionViewDelegateFlowLayout의 sizeForItemAt 메소드를 사용했다. 셀의 너비는 컬렉션 뷰의 크기 만큼 지정했다.
자 그럼 실행해볼까
어라라..? 스크롤도 정상적으로 작동하고, 더미 데이터도 정상적으로 불러온 것 같은데 이미지가 너무 크다.. 뭐가 문제일까..
한 20분 헤맸다가 아차 하고 생각이 듦. 컬렉션 뷰의 Estimate Size를 설정 안해줌 ...
두 가지 방법이 있다.
if let flowlayout = puppyCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowlayout.estimatedItemSize = .zero
}
이렇게 코드로 설정하는 방법과 ( 이 친구는 옵셔널이기 때문에 언래핑 해줘야함 ! )
이렇게 인스펙터에서 지정하는 방법이 있다. 둘 중 편한 걸로 하면 된다. 나는 둘 다 익숙해 지려고 둘 다 공부하는 중이다. 본인 선택임!
그래서 다시 실행하면?
좋아 화면에는 정상적으로 출력 됐는데.. 이미지 크기가 아쉽다. 내가 이미지를 사이즈에 맞게 가공한 것이 아니기 때문이다 .. 그렇다면 .. 또 찾아봐야겠지 ..
구글링 해보니까 정말 간단한 문제다. 이미지뷰의 Content Mode를 수정해주면 된다.
주로 많이 쓰는 것들이 aspectFit, aspectFill, scaleToFill이다.
aspectFit는 비율을 유지하면서 뷰의 사이즈에 맞게 이미지를 늘리는 옵션이다. 이미지가 뷰를 꽉 채우지 못하면 남는 부분은 투명처리 된다.
aspectFill는 비율을 유지하며서 뷰의 사이즈에 맞게 이미지를 꽉 채우는 옵션이다. 이미지의 어떤 부분은 잘려서 보일 수도 있다.
scaleToFill는 전체 이미지가 다 나올 수 있도록 필요하다면 비율을 깨뜨리면서 뷰의 사이즈에 맞게 이미지를 꽉 채우는 옵션이다.
나는 비율을 유지하고 뷰의 사이즈에 맞게 이미지를 꽉 채우고 싶고 잘려보이는건 싫으니까 scaleToFill를 사용해야겠다.
얘도 두가지 방법으로 설정할 수 있다.
import UIKit
class puppyCell: UICollectionViewCell {
@IBOutlet weak var puppyImageView: UIImageView!
@IBOutlet weak var puppyNameLabel: UILabel!
@IBOutlet weak var puppyBreedLabel: UILabel!
@IBOutlet weak var puppyInfoLabel: UILabel!
override func awakeFromNib() {
puppyImageView.contentMode = .scaleToFill
}
func printPuppy(_ puppyList: Puppy) {
puppyImageView.image = UIImage(named: puppyList.imageName)
puppyNameLabel.text = "\(puppyList.name)"
puppyBreedLabel.text = "(\(puppyList.dog))"
puppyInfoLabel.text = "\(puppyList.puppyInfo)"
}
}
이렇게 코드로 해당 이미지뷰의 contentMode 프로퍼티를 이용해서 설정하는 방법과
간단하게 인스펙터에서 설정하는 방법이 있다. 본인 선택이니 편한데로 ~
진짜 끝났나? 실행 해볼까?
사진 크기도 완벽하고 스크롤도 잘 작동하고 ~ 근데 뽀삐를 설명하는 라벨이 잘려서 보인다.. 저건 이미 알고 있으니까 금방 해결할 수 있다.
오토레이아웃을 설정 했을 때 라벨이 잘리는 경우가 있는데 이 때는 라인을 설정하거나 해당 라벨의 Autoshrink를 설정해주면 된다.
이왕 하는거 복습 겸 둘 다 해보자 !🙃
일단 라인 먼저 해보자
import UIKit
class puppyCell: UICollectionViewCell {
@IBOutlet weak var puppyImageView: UIImageView!
@IBOutlet weak var puppyNameLabel: UILabel!
@IBOutlet weak var puppyBreedLabel: UILabel!
@IBOutlet weak var puppyInfoLabel: UILabel!
override func awakeFromNib() {
puppyImageView.contentMode = .scaleToFill
puppyInfoLabel.numberOfLines = 2
}
func printPuppy(_ puppyList: Puppy) {
puppyImageView.image = UIImage(named: puppyList.imageName)
puppyNameLabel.text = "\(puppyList.name)"
puppyBreedLabel.text = "(\(puppyList.dog))"
puppyInfoLabel.text = "\(puppyList.puppyInfo)"
}
}
이렇게 코드로 해당 라벨의 numberOfLines 프로퍼티를 이용해서 설정하는 방법과
이렇게 인스펙터를 이용한 방법이 있다. Line 부분을 수정해주면 된다.
결과는 짜잔~ 라벨의 크기를 넘어가면 잘리는 것이 아니라 저렇게 두 줄로 나온다.
자 그럼 진짜 마지막으로 이제 Autoshrink를 조절하는 방법이다. Autoshrink는 유동적으로 폰트의 사이즈를 바꿔서 출력한다.
자 이렇게 ... 으로 잘려 보이는 친구를 한번 살려보자
import UIKit
class puppyCell: UICollectionViewCell {
@IBOutlet weak var puppyImageView: UIImageView!
@IBOutlet weak var puppyNameLabel: UILabel!
@IBOutlet weak var puppyBreedLabel: UILabel!
@IBOutlet weak var puppyInfoLabel: UILabel!
override func awakeFromNib() {
puppyImageView.contentMode = .scaleToFill
//puppyInfoLabel.numberOfLines = 2
puppyInfoLabel.adjustsFontSizeToFitWidth = true
puppyInfoLabel.minimumScaleFactor = 0.5
}
func printPuppy(_ puppyList: Puppy) {
puppyImageView.image = UIImage(named: puppyList.imageName)
puppyNameLabel.text = "\(puppyList.name)"
puppyBreedLabel.text = "(\(puppyList.dog))"
puppyInfoLabel.text = "\(puppyList.puppyInfo)"
}
}
이렇게 코드로 해당 라벨의 adjustsFontSizeToFitWidth 프로퍼티를 이용해서 설정하는 방법과
이렇게 인스펙터에서 지정하는 방법이 있다. 인스펙터에서의 Autoshrink는 세 가지 경우를 설정할 수 있는데,
1. Minimum Font Scale : 0~1까지 수를 입력해서 얼마나 줄어들지 결정한다. 0.5 라면 50%까지 줄어든다.
2. Minimum Font Size : 폰트 사이즈가 얼마나 줄어들지 결정
3. Tighten Letter Spacing = 자간을 줄임.
진짜 끝이다 !! 재밌었다 재밌었어 ~ 다음에는 한 줄이 아니라 그리드 형식으로 만들어봐야겠다 뿅
'Swift > UIkit' 카테고리의 다른 글
[UIkit] 오토 레이아웃 파헤치기 - 2 ⛏️ (0) | 2023.03.24 |
---|---|
[UIkit] 오토 레이아웃 파헤치기 - 1 ⛏️ (0) | 2023.03.24 |
[UIkit] Grid 형태의 CollectionView 만들어보기 !! (0) | 2023.03.23 |
[UIkit] CollectionView 알아보기 🔎 !! (0) | 2023.03.23 |
[UIkit] IBOutlet / IBAction (0) | 2023.03.20 |