Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ITDS-71] feature: Skeleton 개선 및 적용 #62

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public final class BannerCell: UICollectionViewCell {
public func setData(_ data: BannerCellData) {
bannerImage.kf.setImage(
with: URL(string: data.bannerImageURL),
placeholder: UIImage.image(with: MozipColor.gray300)
placeholder: UIImage.image(with: MozipColor.gray10)
)
bannerImage.flex.markDirty()
setNeedsLayout()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ private extension BannerCollectionView {
self.isPagingEnabled = true
self.showsHorizontalScrollIndicator = false
self.layer.cornerRadius = Metric.cornerRadius
register(BannerSkeletonCell.self)
register(BannerCell.self)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// BannerSkeletonCell.swift
// DesignSystem
//
// Created by 한상진 on 1/5/25.
// Copyright © 2025 MOZIP. All rights reserved.
//

import UIKit

import PinLayout

public final class BannerSkeletonCell: UICollectionViewCell {

// MARK: - Properties
private let bannerSkeletonView = SkeletonView()

// MARK: - Initializer
public override init(frame: CGRect) {
super.init(frame: frame)

contentView.addSubview(bannerSkeletonView)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Drawing Cycle
public override func layoutSubviews() {
super.layoutSubviews()

bannerSkeletonView.pin.all()
}
}
3 changes: 0 additions & 3 deletions Targets/DesignSystem/Sources/Component/Home/FABButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ public final class FABButton: UIView {
owner.plusIcon.image = bool ?
DesignSystemAsset.xmark.image.withTintColor(MozipColor.primary500, renderingMode: .alwaysOriginal) :
DesignSystemAsset.plus.image.withTintColor(MozipColor.white, renderingMode: .alwaysOriginal)

owner.rootFlexContainer.flex.layout()
}
.disposed(by: disposeBag)
}
Expand All @@ -92,7 +90,6 @@ public final class FABButton: UIView {
private func setupLayer() {
rootFlexContainer.pin.all()
rootFlexContainer.flex.layout()
pin.size(CGSize(width: 72, height: 72))
}

private func setupShadow() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public final class FABSubButton: UIView {
self.layer.cornerRadius = 8
self.icon.image = iconImage
self.title.updateTextKeepingAttributes(title)
self.isHidden = true
}

required init?(coder: NSCoder) {
Expand Down
98 changes: 0 additions & 98 deletions Targets/DesignSystem/Sources/Component/Home/HomeSkeleton.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import RxDataSources

public final class PostCollectionView: UIView {

// MARK: - Properties
private enum Metric {
static let cellWidth: CGFloat = 148
static let cellHeight: CGFloat = 236
Expand All @@ -28,43 +29,63 @@ public final class PostCollectionView: UIView {
static let zero: CGFloat = 0
}

private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init())

public var cellTapObservable: Observable<IndexPath> {
collectionView.rx.itemSelected.asObservable()
}

public let headerTapRelay: PublishRelay<IndexPath> = .init()

private let sectionsRelay: BehaviorRelay<[PostSection]> = .init(value: [])
private let sectionsRelay: BehaviorRelay<[PostSection]> = .init(value: Array(
repeating: .init(original: .stub(), items: Array(repeating: .stub(), count: 3)),
count: 3
))

private var isLoading = true
private let disposeBag = DisposeBag()
private var headerDisposeBag = DisposeBag()

private lazy var dataSource = RxCollectionViewSectionedReloadDataSource<PostSection>(
configureCell: { (dataSource, collectionView, indexPath, item) in
let cell: PostCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
cell.setData(item)
return cell
configureCell: { [weak self] (dataSource, collectionView, indexPath, item) in
guard let self else { return UICollectionViewCell() }

if isLoading {
let cell: PostSkeletonCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
return cell
} else {
let cell: PostCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
cell.setData(item)
return cell
}
Comment on lines +53 to +60
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스켈레톤 표출 조건 분기처리가 모든 곳에 들어가서 작업량이 많았을 텐데 고생하셨습니다 👍

},
configureSupplementaryView: { [weak self] (dataSource, collectionView, kind, indexPath) in
guard let self = self else { return UICollectionReusableView() }
let header: PostHeader = collectionView.dequeueReusableSupplementaryView(
ofKind: UICollectionView.elementKindSectionHeader,
for: indexPath)
let sectionModel = dataSource.sectionModels[indexPath.section]
header.setData(title: sectionModel.kind.sectionTitle, summary: sectionModel.kind.summary)
header.tapObservable
.map { indexPath }
.bind(to: headerTapRelay)
.disposed(by: headerDisposeBag)
return header
})

// MARK: - UI
private let collectionView: UICollectionView
private let flowLayout = UICollectionViewFlowLayout()
guard let self else { return UICollectionReusableView() }

if isLoading {
let header: PostSkeletonHeader = collectionView.dequeueReusableSupplementaryView(
ofKind: UICollectionView.elementKindSectionHeader,
for: indexPath
)
return header
} else {
let header: PostHeader = collectionView.dequeueReusableSupplementaryView(
ofKind: UICollectionView.elementKindSectionHeader,
for: indexPath
)
let sectionModel = dataSource.sectionModels[indexPath.section]
header.setData(title: sectionModel.kind.sectionTitle, summary: sectionModel.kind.summary)
header.tapObservable
.map { indexPath }
.bind(to: headerTapRelay)
.disposed(by: headerDisposeBag)
return header
}
}
)

// MARK: - Initializers
public init() {
collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
super.init(frame: .zero)
setupCollectionView()
setupViewHierarchy()
Expand All @@ -90,16 +111,24 @@ public final class PostCollectionView: UIView {
public func setupData(_ data: [PostSection]) {
headerDisposeBag = DisposeBag()
sectionsRelay.accept(data)
isLoading = false
collectionView.reloadData()
}

private func setupCollectionView() {
collectionView.setCollectionViewLayout(configureCollectionViewLayout(), animated: false)
collectionView.showsHorizontalScrollIndicator = false
collectionView.isScrollEnabled = false
collectionView.register(PostCell.self)
collectionView.register(PostSkeletonCell.self)
collectionView.register(
PostHeader.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader)
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader
)
collectionView.register(
PostSkeletonHeader.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader
)

sectionsRelay
.bind(to: collectionView.rx.items(dataSource: dataSource))
Expand Down
59 changes: 59 additions & 0 deletions Targets/DesignSystem/Sources/Component/Home/PostSkeletonCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// PostSkeletonCell.swift
// DesignSystem
//
// Created by 한상진 on 1/9/25.
// Copyright © 2025 MOZIP. All rights reserved.
//

import UIKit

import FlexLayout
import PinLayout

public final class PostSkeletonCell: UICollectionViewCell {

// MARK: - Properties
private enum Metric {
static let verticalSpacing: CGFloat = 8
static let labelHeight: CGFloat = 16
static let remainDayHeight: CGFloat = 24
}

private let thumbnailImageSkeletonView = SkeletonView()
private let titleLabelSkeletonView1 = SkeletonView()
private let titleLabelSkeletonView2 = SkeletonView()
private let remainDayTagSkeletonView = SkeletonView()

// MARK: - Initializer
public override init(frame: CGRect) {
super.init(frame: frame)

setupViews()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Drawing Cycle
public override func layoutSubviews() {
super.layoutSubviews()

contentView.flex.layout()
}
}

// MARK: - Private Extenion
private extension PostSkeletonCell {
func setupViews() {
contentView.flex
.gap(Metric.verticalSpacing)
.define {
$0.addItem(thumbnailImageSkeletonView).width(100%).aspectRatio(1)
$0.addItem(titleLabelSkeletonView1).height(Metric.labelHeight)
$0.addItem(titleLabelSkeletonView2).height(Metric.labelHeight)
$0.addItem(remainDayTagSkeletonView).width(30%).height(Metric.remainDayHeight)
}
}
}
Loading