✅ 이번시간에는 스크롤 뷰에 대해서 알아보자.
사실 예전에 스크롤 뷰를 급하게 사용할 일이 있어서 구글링해서 사용했던 적이 있는데, 그때 정확하게 이해한 것이 아니라서 다른곳에 적용할때 안되었던 기억이... 그럼 아무튼 다시 공부해보도록 할까?
(들어가기 앞서...)
강의에서는 Nested ScrollView(네스티드 스크롤 뷰) 라는 용어를 많이 사용하는데, 네스티드 스크롤 뷰란, 이중 스크롤 뷰라는 의미로, 스크롤 뷰에서 스크롤 뷰를 하나 더 추가한 것을 의미한다. 스크롤 뷰가 즉, 2개 있다고 생각하면 편할듯 하다.
✅ 목차
1. 스크롤 뷰 기본 가이드 - 상하로 움직이기 (Vertical Scroll View)
2. 스크롤 뷰 Advanced - 좌우로도 움직이기 (Horizontal Scroll View)
✅ 그럼 이제 진짜 시작!!
1️⃣ Vertical Scroll View
일단 스토리보드에서 뷰 컨트롤러에 스크롤 뷰를 추가해!
그 이후에 레이아웃을 잡아줄건데, 그냥 상하좌우 0,0,0,0 으로 세팅하면 아래의 그림과 같이 빨간불이 들어오는 것을 볼 수 있어.
이전에는 안들어오는데 조금 이상하지?
스크롤 뷰에서는 자기 뷰 말고 컨텐츠 뷰에 대한 제약조건도 설정해줘야 빨간색이 없어지니까 일단 넘어가면서 보자
우리가 레이아웃까지 잡았다면 현재 이런 상태일거야.
일단은 우측 스크롤 뷰 쪽에서 Content Layout Guide를 꺼주도록 하자.
일단 Guide를 끄면 위의 사진과 다르게 스크롤 뷰에 Guide 두개가 사라진 것을 눈으로 확인할 수 있어.
여기부터 조금 헷갈릴 수가 있어서 스크롤 뷰 안에 있는 뷰를 일반 뷰, 스크롤 뷰를 포함하는 뷰를 슈퍼 뷰라고 부를게.
1. 그 다음에는 위의 사진처럼 View(일반 뷰)를 추가하고 레이아웃은 스크롤 뷰와 일반 뷰를 연결해서, leading과 top을 줘.
이제부터 유의해서 봐야해..!
2. ❗️(주의) 그리고 일반 뷰를 슈퍼뷰에 레이아웃을 연결해서 Equal Width를 설정해주고,
3. 그 다음에는 일반 뷰를 자기 자신으로 연결해서 Height를 잡아줘.
(⭐️ 이거 아주 중요한 역할인데, 이 크기를 조정하면 스크롤 뷰의 전체적인 길이를 설정해 줄 수 있어..!)
4. 그 다음에는 일반 뷰를 다시 스크롤 뷰로 레이아웃을 연결해서 트레일링과 바텀을 주기.
그럼 가장 기초적인 세팅이 끝나 -> 레이아웃 경고인 빨간색이 사라져!!
5. 일반 뷰를 슈퍼뷰에 Equal Height로 연결해주기
그런데 여기서 일반 뷰의 경우에는 컨텐츠의 성격을 가져서 높이가 바뀔 수도 있잖아?
6. 그래서 일반 뷰와 슈퍼 뷰를 연결한 제약조건을 클릭해서 priority값을 낮게 수정해 줘야해
이렇게 수정해 주면 돼. 임의의 값을 줘도, 아니면 여기 정해진 값을 줘도 돼.
✅ 우선 6번까지 올바르게 수행했다면 스크롤 뷰는 정상적으로 작동할 예정!! 아래에는 눈으로 확인하기 위해서 작성한 부분이야
7. 그 다음에는 다음과 같이 imageView와 StackView를 추가해줘.
8. 눈으로 확인하기 쉬워야하니까 이미지 뷰에는 초록색 배경을 스택뷰에는 뷰를 추가한 후 그 뷰의 배경색을 보라색으로 설정해 주기.
9. 아 근데, 스토리보드에서 우리가 시뮬레이터에서 스크롤 하듯이 보고 싶을 때가 있지?
✅ 그럼 좌우로도 이동하게끔 만들어 볼까?
2️⃣. Horizontal Scroll View
1. 우선 스택 뷰안에 3개의 뷰를 넣을거야.
스택뷰의 높이가 현재 600으로 설정되어 있어 -> 이건 헤제해도 돼. 그 이유는 안의 뷰들의 높이 따라서 자동으로 설정이 돼
2. 넣은 3개의 뷰에 Heigth를 자기 자신으로 레이아웃을 잡아서 각각 200으로 설정해주기
3. 이제 뷰 3개를 동시에 지우고 스택 뷰 안에 컨테이너 뷰를 넣는다.
컨테이너 뷰 2개를 스택 뷰 안에 넣은 후 각 컨테이너 뷰의 높이를 잡아줄건데 200으로 한다.
컨테이너 뷰를 넣을때, 이미 생성된 컨테이너 뷰를 복사 붙여넣기를 수행하면, 파란색과, 노란색으로 표현된 뷰 컨트롤러가 생성되지 않는데, 그럴때는 뷰 컨트롤러를 하나 만든 후에, 컨테이너 뷰 중 뷰 컨틀롤러를 갖고 있지 않는 것에 연결한다.
이때 연결할때는 Embed 를 세그로 주면 된다. -> 뷰 컨트롤러 모양이 아래와 같이 작게 바뀐다.
4. 다음에는 레이블과 컬렉션 뷰를 뷰에 넣고 컬렉션 뷰 안에 이미지 뷰를 추가한다.
레이아웃은 레이블은 탑(6), 트레일링(8)
컬렉션 뷰는 트레일링(0), 리딩(0), 바텀(6), 높이(160)으로 준다.
❗️ 여기서 노란색 배경을 가진 컨테이너를 만들때 오류가 발생해서, 그냥 파란색 복붙한 후 사용했다.
5. 다음으로는 커스텀 클래스를 연결하고, 커스텀 클래스에 IBOulet을 타이틀을 연결한다.
그리고 컬렉션 뷰는 델리게이트랑, 데이터소스 뷰 컨트롤러 위임하는거 잊지말기.
😀 RecommendListViewController.swift 코드!
//
// RecommendListViewController.swift
// MyNetflix
//
// Created by Hamlit Jason on 2021/06/29.
//
import UIKit
class RecommendListViewController: UIViewController {
@IBOutlet weak var sectionTitle: UILabel!
let viewModel = RecommentListViewModel()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateUI()
}
func updateUI() {
sectionTitle.text = viewModel.type.title
}
}
extension RecommendListViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return viewModel.numOfItems
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RecommendCell", for: indexPath) as? RecommendCell else {
return UICollectionViewCell()
}
let movie = viewModel.item(at: indexPath.item)
cell.updateUI(movie: movie)
return cell
}
}
extension RecommendListViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 120, height: 160)
}
}
class RecommentListViewModel {
enum RecommendingType {
case award
case hot
case my
var title: String {
switch self {
case .award: return "아카데미 호평 영황"
case .hot: return "취한저격 HOT 콘텐츠"
case .my: return "내가 찜한 콘텐츠"
}
}
}
private (set) var type: RecommendingType = .my
private var items: [DummyItem] = []
var numOfItems: Int {
return items.count
}
func item(at index: Int) -> DummyItem {
return items[index]
}
func updateType(_ type: RecommendingType) {
self.type = type
}
func fetchItems() {
self.items = MovieFetcher.fetch(type)
}
}
class RecommendCell: UICollectionViewCell {
@IBOutlet weak var thumbnailImage: UIImageView!
func updateUI(movie: DummyItem) {
thumbnailImage.image = movie.thumbnail
}
}
class MovieFetcher {
static func fetch(_ type: RecommentListViewModel.RecommendingType) -> [DummyItem] {
switch type {
case .award:
let movies = (1..<10).map { DummyItem(thumbnail: UIImage(named: "img_movie_\($0)")!) }
return movies
case .hot:
let movies = (10..<19).map { DummyItem(thumbnail: UIImage(named: "img_movie_\($0)")!) }
return movies
case .my:
let movies = (1..<10).map { $0 * 2 }.map { DummyItem(thumbnail: UIImage(named: "img_movie_\($0)")!) }
return movies
}
}
}
struct DummyItem {
let thumbnail: UIImage
}
7. 다음은 UpComingViewController.swift 를 만든 후에 아래의 코드와 같이 작성한다.
import UIKit
class UpComingViewController : UIViewController {
var awardRecommendListViewController: RecommendListViewController!
var hotRecommendListViewController: RecommendListViewController!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "award" {
let destinationVC = segue.destination as? RecommendListViewController
awardRecommendListViewController = destinationVC
awardRecommendListViewController.viewModel.updateType(.award)
awardRecommendListViewController.viewModel.fetchItems()
} else if segue.identifier == "hot" {
let destinationVC = segue.destination as? RecommendListViewController
hotRecommendListViewController = destinationVC
hotRecommendListViewController.viewModel.updateType(.hot)
hotRecommendListViewController.viewModel.fetchItems()
}
}
}
8. 이후에는 세그를 지정해 줘야겠지?
파란색 세그는 award, 노란색 세그는 hot으로 세그를 준다.
❗️시뮬레이터를 돌렸을 때, 나는 화면이 제대로 표시되지 않았는데, 코드를 다시 작성했더니 되었다.. 혹시라도 안된다면, 내 코드를 복붙해서 스토리보드 쪽을 바꿔볼 것!
이러면 일단 끝!!
이후에는 파이어베이스를 이용해서 검색어를 받아오는 작업을 해보자!
(추가)
스크롤 뷰에서 RecommendListViewController 가 어떻게 구성되었는지 궁금하다면...
여기서 RecommendListViewController부분을 참고하길 바래
'Archive > 패캠(올인원)' 카테고리의 다른 글
🎬 ch17 Netflix 확장앱 코드리뷰(firebase, kingfisher) + ch15 (0) | 2021.07.05 |
---|---|
ch17 SPM과 CocoaPod 충돌시 해결 (0) | 2021.07.02 |
😼 ch15 검색을 이용해 서버에서 데이터를 받아와 파싱까지! (0) | 2021.06.29 |
ch15 escaping과 non-escaping 클로저에 대해서 알아보자 (0) | 2021.06.29 |
🍜 ch15 인스턴스 메소드 vs 타입메소드 (0) | 2021.06.29 |