안녕하세요. Skillist입니다.
App 화면 구성도 얼마 안남았습니다. 같이, 열심히 개발해봐요
------------------------------------------------------------------------------------------------------------------
11라인 : Today 화면에선, UICollectionViewController로 구현해봤죠? App 화면은 UIViewController에 collectionView를 추가하여 구현해봤습니다.
15라인 : collectionView입니다. delegate, datasource를 self로 지정하고, cell과 header를 등록했어요.
29라인 : compositional layout입니다. 이번엔 프로퍼티로 구현해봤어요
39라인 : 3가지의 cell을 구현하려다가, 2가지 cell만 구현했고, 이에 따른 주석처리 입니다.
------------------------------------------------------------------------------------------------------------------
viewDidLoad입니다.
56라인 :collectionView를 추가하고, SnapKit으로 위치를 배치 합니다.
71라인 : viewWillAppear에서 네비게이션바를 설정합니다.
왜 viewWillAppear에서 네비게이션바를 설정하나면, App 화면에서 app상세화면으로 이동 후 다시 App 화면으로 되돌아왔을때, 네비게이션바를 유지하기 위함입니다.
------------------------------------------------------------------------------------------------------------------
이번엔 section을 설정해볼까요
LargeItemSection입니다
101라인 : group의 사이즈를 다음과 같이 설정했습니다.
width : absolute를 통해, margin값을 제외한 width를 계산하여 지정했습니다.
height : estimated를 통해 약 350정도로 지정했습니다.
105라인 : 그룹에는 1개의 아이템만 가지도록 설정했습니다.
106라인 : 그룹간의 여백을 위해 좌우 inset을 설정했습니다.
110라인 : 스크롤을 그룹페이징으로 설정하여, 그룹 페이지를 구현했습니다.
111라인 : section의 inset입니다. 좌우의 값을 groupMargin 값인 6보다 큰 10으로 설정하여 좌우에 다른 그룹이 살짝 보이도록 구현했어요.
------------------------------------------------------------------------------------------------------------------
이번엔 smallItemSection입니다.
123라인 : 아이템 별로 상하 여백이 필요하여, inset을 설정했어요.
126라인 : group의 사이즈를 계산하여 설정했습니다.
127라인 : 3개의 아이템을 1개의 그룹으로 설정했어요.
132라인 : 스크롤을 그룹 별 페이지로 설정했습니다.
133라인 : inset을 설정하여, 좌우에 다른 그룹이 살짝 보이도록 설정했어요.
136라인 : header를 추가했습니다.
144, 147라인 : header를 설정했습니다.
section을 구현하다보면 일정 코드가 반복된다는것을 느낄수 있어요. 코드는 길지만??, 반복됩니다!
------------------------------------------------------------------------------------------------------------------
이번엔 UICollectionViewDataSource 입니다
162라인 : section의 개수를 리턴합니다.
166라인 : section이 가지고 있는 아이템의 개수를 리턴합니다
173라인 : section의 타입에 따라, cell을 리턴합니다. 우리가 만든 cell들을 리턴합니다.
이번엔 header인데요, header를 하나의 section에서 사용하니, 분기 처리는 하지 않았어요.
------------------------------------------------------------------------------------------------------------------
이렇게 하여, AppViewController을 구현해봤습니다.
코드를 구현할땐, 어마어마하게 코딩한것 같은데, 막상 글로 작성해보니 별거 없네요;;;;;;;;
고생하셨습니다~ 다음에 또 만나요
잘못되거나 부족한 내용 등, 피드백 감사합니다!
Skillist의 AppleAppStore 프로젝트
https://github.com/DeveloperSkillist/AppleAppStoreCloneCode
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 전체 코드 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
import UIKit
import SnapKit
class AppViewController: UIViewController {
private var sections: [AppItem] = []
private lazy var collectionView: UICollectionView = {
var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
//cell
collectionView.register(AppLargeItemCollectionViewCell.self, forCellWithReuseIdentifier: "AppLargeItemCollectionViewCell")
collectionView.register(AppSmallItemsCollectionViewCell.self, forCellWithReuseIdentifier: "AppSmallItemsCollectionViewCell")
//header
collectionView.register(AppSmallItemCollectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "AppSmallItemCollectionHeaderView")
return collectionView
}()
private lazy var layout: UICollectionViewLayout = {
return UICollectionViewCompositionalLayout { [weak self] section, _ -> NSCollectionLayoutSection? in
guard let sectionType = self?.sections[section].type else {
return nil
}
switch sectionType {
case .largeItem:
return self?.createLargeItemSection()
// case .mediumItem:
// return self?.createMediumItemInfoSection()
// return nil
case .smallItem:
return self?.createSmallItemSection()
}
}
}()
override func viewDidLoad() {
super.viewDidLoad()
setupLayout()
setupData()
}
private func setupLayout() {
view.backgroundColor = .systemBackground
[
collectionView
].forEach {
view.addSubview($0)
}
collectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setupNavigationBar()
}
private func setupNavigationBar() {
navigationItem.title = "app_title".localized
navigationController?.navigationBar.prefersLargeTitles = true
//large title text 설정
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.label]
//TODO: large title에 accound 추가
// let accountProfileView = AccountProfileView() navigationController?.navigationBar.addSubview(accountProfileView)
// accountProfileView.snp.makeConstraints {
// $0.trailing.equalToSuperview().inset(16)
// $0.bottom.equalToSuperview().inset(10)
// $0.width.height.equalTo(30)
// }
}
}
extension AppViewController {
private func createLargeItemSection() -> NSCollectionLayoutSection {
let sectionMargin: CGFloat = 10
let groupMargin: CGFloat = 6
//item
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
// item.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0)
//group
let groupSize = NSCollectionLayoutSize(
widthDimension: .absolute(view.frame.width - (sectionMargin * 2)),
heightDimension: .estimated(350)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
group.contentInsets = .init(top: 0, leading: groupMargin, bottom: 0, trailing: groupMargin)
//section
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPaging
section.contentInsets = .init(top: sectionMargin + groupMargin, leading: sectionMargin, bottom: sectionMargin + groupMargin, trailing: sectionMargin)
return section
}
private func createSmallItemSection() -> NSCollectionLayoutSection {
//item
let itemMargin: CGFloat = 4
let sectionMargin: CGFloat = 12
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 0, leading: itemMargin, bottom: 0, trailing: itemMargin)
//group
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(view.frame.width - (sectionMargin * 2)), heightDimension: .estimated(250))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 3)
// group.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0)
//section
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPaging
section.contentInsets = .init(top: sectionMargin + itemMargin, leading: sectionMargin, bottom: sectionMargin + itemMargin, trailing: sectionMargin)
//header
let sectionHeader = self.createSmallItemSectionHeader()
section.boundarySupplementaryItems = [sectionHeader]
return section
}
private func createSmallItemSectionHeader() -> NSCollectionLayoutBoundarySupplementaryItem {
//section header 사이즈
let layoutSectionHeaderSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(40))
//section header Layout
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: layoutSectionHeaderSize, elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
return sectionHeader
}
}
extension AppViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let detailVC = DetailViewController()
//TODO detailVC item 설정
navigationController?.pushViewController(detailVC, animated: true)
}
}
extension AppViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return sections.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let section = sections[section]
return section.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let sectionType = sections[indexPath.section].type
switch sectionType {
case .largeItem:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AppLargeItemCollectionViewCell", for: indexPath) as? AppLargeItemCollectionViewCell else {
return UICollectionViewCell()
}
guard let item = sections[indexPath.section].items[indexPath.row] as? AppLargeItem else {
return UICollectionViewCell()
}
cell.setup(item: item)
return cell
// case .mediumItem:
// return UICollectionViewCell()
case .smallItem:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AppSmallItemsCollectionViewCell", for: indexPath) as? AppSmallItemsCollectionViewCell else {
return UICollectionViewCell()
}
guard let item = sections[indexPath.section].items[indexPath.row] as? AppSmallItem else {
return UICollectionViewCell()
}
cell.setup(item: item)
return cell
}
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
guard let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "AppSmallItemCollectionHeaderView", for: indexPath) as? AppSmallItemCollectionHeaderView else {
return UICollectionReusableView()
}
let item = sections[indexPath.section]
headerView.setup(mainText: item.mainText)
return headerView
}
return UICollectionReusableView()
}
}
extension AppViewController {
private func setupData() {
sections = [
AppItem(
type: .largeItem,
items: [
AppLargeItem(
subText: "2021 App Store Awards",
mainText: "Skillist Project",
mainInfoText: "이게 Skillist다",
subTextColor: .link,
mainTextColor: .label,
mainInfoTextColor: .label,
imageURL: nil,
image: RandomData.image
),
AppLargeItem(
subText: "2021 App Store Awards",
mainText: "Skillist Project",
mainInfoText: "이게 스터디다",
subTextColor: .link,
mainTextColor: .label,
mainInfoTextColor: .label,
imageURL: nil,
image: RandomData.image
),
AppLargeItem(
subText: "2021 App Store Awards",
mainText: "Skillist Project",
mainInfoText: "이것이 클론코딩이다",
subTextColor: .link,
mainTextColor: .label,
mainInfoTextColor: .label,
imageURL: nil,
image: RandomData.image
)
],
subText: "",
mainText: "",
mainInfoText: ""
),
AppItem(
type: .smallItem,
items: [
AppSmallItem(
mainText: "랜덤 skillist",
subText: "지금 내 기분은",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "마치 08년도 베이식",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "워~!",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "데리고 와봐",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "네가 랩 잘한다는",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "나는 조광일의 랩을 따라",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
)
],
mainText: "08 베이식",
isAllShowButton: RandomData.boolean
),
AppItem(
type: .smallItem,
items: [
AppSmallItem(
mainText: "랜덤 skillist",
subText: "난 모두가 잘 되길 빌어",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "이 말이 거짓말 같다 해도",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "확실한건 더이상",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "상처를 주기 싫어",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "이제와서 보니 세상이",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "우릴 만들었단 생각이 들어",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "얻은것보다도 잃은게",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "많은것 같어 사실 너도 ",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
),
AppSmallItem(
mainText: "랜덤 skillist",
subText: "알잖아",
isInAppPurchase: RandomData.boolean,
isInstalled: RandomData.boolean,
imageURL: nil,
image: RandomData.image
)
],
mainText: "내일이 오면",
isAllShowButton: RandomData.boolean
),
AppItem(
type: .largeItem,
items: [
AppLargeItem(
subText: "너와 나의 메모리 - 베이식",
mainText: "2007년의 슈퍼루키",
mainInfoText: "국힙의 미래가 내 대명사",
subTextColor: .link,
mainTextColor: .label,
mainInfoTextColor: .label,
imageURL: nil,
image: RandomData.image
),
AppLargeItem(
subText: "너와 나의 메모리 - 쿤타",
mainText: "좁은 골목길을 들어가면",
mainInfoText: "빛이 반만 드는 창에 우리가보여",
subTextColor: .link,
mainTextColor: .label,
mainInfoTextColor: .label,
imageURL: nil,
image: RandomData.image
),
AppLargeItem(
subText: "너와 나의 메모리 - 쿤타",
mainText: "감옥같은 방에 빛이 들어오면",
mainInfoText: "막연한 오늘의 희망이 잠깐 보여",
subTextColor: .link,
mainTextColor: .label,
mainInfoTextColor: .label,
imageURL: nil,
image: RandomData.image
)
],
subText: "",
mainText: "",
mainInfoText: ""
)
]
}
}
'iOS 개발 > Apple App Store 클론 코딩' 카테고리의 다른 글
AppleAppStore - 9. Search 화면 구현 (0) | 2022.01.02 |
---|---|
AppleAppStore - 8. 결과물 중간 점검 (0) | 2021.12.24 |
AppleAppStore - 6. App 화면의 Cell 구현 (0) | 2021.12.17 |
AppleAppStore - 5. Today 화면 구현 (0) | 2021.12.16 |
AppleAppStore - 4. Today 화면의 cell 구현 2 (0) | 2021.12.16 |