Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8d911e1
[Add] Skeleton Framework 설치 (#187)
yurim830 Jun 10, 2025
4778c02
[Refactor] 기존 skeletonView 삭제, CollectionView skeleton 세팅 (#187)
yurim830 Jun 11, 2025
d248dd1
[Refactor] .loading ImageStatus 추가, SpotListCell에 Skeleton 설정 (#187)
yurim830 Jun 11, 2025
a234ccf
[Add,Feat] Skeleton Header 구현 (#187)
yurim830 Jun 11, 2025
2f014cb
[Fix] 데이터 set된 후 컬렉션뷰 top으로 이동하는 로직 수정 (#187)
yurim830 Jun 11, 2025
53a9a01
[Chore] 코드 순서, 주석 정리, 스켈레톤 헤더 delegate 설정 (#187)
yurim830 Jun 11, 2025
6f03158
[Fix] 스켈레톤 설정 오류 수정 (#187)
yurim830 Jun 11, 2025
b1a5be3
[Feat] NoMatching Cell 스켈레톤뷰 설정 (#187)
yurim830 Jun 12, 2025
7eef320
[Chore] 불필요한 코드 삭제 및 스켈레톤 확인용 디바운서 설정 (#187)
yurim830 Jun 12, 2025
3198dfa
[Add] SpotListSkeletonHeader (#187)
yurim830 Jun 12, 2025
38bbf19
[Chore] 브랜치 최신화 및 컨플릭 해결 (#187)
yurim830 Jun 12, 2025
a2d517b
[Fix] 토스트 버튼 누르면 스켈레톤 애니메이션 시작 (#187)
yurim830 Jun 12, 2025
fb30e0f
[Chore] 장소 도토리 0개일 때 UI hidden처리 (#187)
yurim830 Jun 12, 2025
ce8c90e
[Feat] 광고 셀 스켈레톤 구현 (#187)
yurim830 Jun 12, 2025
1de1429
[Chore] 스켈레톤 애니메이션 UI 수정 (#187)
yurim830 Jun 12, 2025
a11c908
[Chore] 스켈레톤 최소시간 1초로 수정 (#187)
yurim830 Jun 12, 2025
fc872dc
[Feat] 프로필 북마크 스켈레톤 구현 (#187)
yurim830 Jun 12, 2025
b56c910
[Chore] 프로필 더미데이터 추가 (#187)
yurim830 Jun 12, 2025
296f740
[Fix] 잘못 호출된 startSkeleton 삭제 (#187)
yurim830 Jun 12, 2025
59e8fdd
[Refactor] 반복되는 스켈레톤 설정 함수 UIKit extension 메소드로 통합 (#187)
yurim830 Jun 12, 2025
0bbd6c8
[Fix] 타이밍 문제 해결 (#187)
yurim830 Jun 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
156AE67B2DE0F37100AE800D /* NoMatchingSpotHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156AE67A2DE0F37100AE800D /* NoMatchingSpotHeader.swift */; };
156D92602D6570A90037F8F1 /* PatchProfileRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 156D925F2D6570A90037F8F1 /* PatchProfileRequest.swift */; };
157135B12DC2A259008C84F9 /* SpotListCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157135B02DC2A259008C84F9 /* SpotListCollectionViewFlowLayout.swift */; };
1576806E2DFA06A70079B255 /* SpotListSkeletonHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1576806D2DFA06A70079B255 /* SpotListSkeletonHeader.swift */; };
157C061F2DC5E7CE000B04AF /* SpotTagType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157C061E2DC5E7CE000B04AF /* SpotTagType.swift */; };
157C06212DC5EBB4000B04AF /* SpotTagButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157C06202DC5EBB4000B04AF /* SpotTagButton.swift */; };
157C0AC12DC689E7000B04AF /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 157C0AC02DC689E7000B04AF /* UIImage+.swift */; };
Expand All @@ -74,6 +75,7 @@
15AA6D172D68B4EF008021C6 /* SpotListErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15AA6D162D68B4EF008021C6 /* SpotListErrorType.swift */; };
15AA6D1A2D68C4AD008021C6 /* ReviewVerificationErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15AA6D192D68C4AD008021C6 /* ReviewVerificationErrorType.swift */; };
15ABCBFC2DF775E8000269F7 /* SpotImageStatusType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15ABCBFB2DF775E8000269F7 /* SpotImageStatusType.swift */; };
15AD50982DF8725200166D9D /* SkeletonView in Frameworks */ = {isa = PBXBuildFile; productRef = 15AD50972DF8725200166D9D /* SkeletonView */; };
15BFF8B92D665BAD00909C4F /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 15BFF8B52D665A5B00909C4F /* Debug.xcconfig */; };
15BFF8BA2D665BAD00909C4F /* Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 15BFF8B62D665A5B00909C4F /* Release.xcconfig */; };
15C0ED522D429F7400C9ED83 /* SkeletonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15C0ED512D429F7400C9ED83 /* SkeletonView.swift */; };
Expand Down Expand Up @@ -329,6 +331,7 @@
156AE67A2DE0F37100AE800D /* NoMatchingSpotHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoMatchingSpotHeader.swift; sourceTree = "<group>"; };
156D925F2D6570A90037F8F1 /* PatchProfileRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchProfileRequest.swift; sourceTree = "<group>"; };
157135B02DC2A259008C84F9 /* SpotListCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotListCollectionViewFlowLayout.swift; sourceTree = "<group>"; };
1576806D2DFA06A70079B255 /* SpotListSkeletonHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotListSkeletonHeader.swift; sourceTree = "<group>"; };
157C061E2DC5E7CE000B04AF /* SpotTagType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotTagType.swift; sourceTree = "<group>"; };
157C06202DC5EBB4000B04AF /* SpotTagButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotTagButton.swift; sourceTree = "<group>"; };
157C0AC02DC689E7000B04AF /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -563,6 +566,7 @@
748D6F4D2D2BC76C007690B4 /* GoogleSignIn in Frameworks */,
74B25C252D2FD30A008BDCB7 /* Lottie in Frameworks */,
748D6F4A2D2BC52B007690B4 /* Kingfisher in Frameworks */,
15AD50982DF8725200166D9D /* SkeletonView in Frameworks */,
746AA0342DAD0EE300E28B66 /* FirebaseCrashlytics in Frameworks */,
74770C192DF1DA97005D4165 /* GoogleMobileAds in Frameworks */,
748D6F412D2BC482007690B4 /* Moya in Frameworks */,
Expand Down Expand Up @@ -649,6 +653,7 @@
1547A6EE2D33854B00E96616 /* SpotListCollectionViewCell.swift */,
15A246192DE7A9E500469272 /* NoMatchingSpotListCollectionViewCell.swift */,
15A3F6AB2D37AB7B00577E16 /* SpotListCollectionViewHeader.swift */,
1576806D2DFA06A70079B255 /* SpotListSkeletonHeader.swift */,
156AE67A2DE0F37100AE800D /* NoMatchingSpotHeader.swift */,
);
path = Cell;
Expand Down Expand Up @@ -1745,6 +1750,7 @@
74D831912D6878A5003384C6 /* AmplitudeSwift */,
746AA0332DAD0EE300E28B66 /* FirebaseCrashlytics */,
74770C182DF1DA97005D4165 /* GoogleMobileAds */,
15AD50972DF8725200166D9D /* SkeletonView */,
);
productName = "ACON-iOS";
productReference = 74AB6EB02D2796D30094F873 /* ACON-iOS.app */;
Expand Down Expand Up @@ -1784,6 +1790,7 @@
74D831902D6878A5003384C6 /* XCRemoteSwiftPackageReference "Amplitude-Swift" */,
746AA0302DAD0EE300E28B66 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
74C1934B2DEF8909005FB3C7 /* XCRemoteSwiftPackageReference "swift-package-manager-google-mobile-ads" */,
15AD50962DF8725200166D9D /* XCRemoteSwiftPackageReference "SkeletonView" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = 74AB6EB12D2796D30094F873 /* Products */;
Expand Down Expand Up @@ -2042,6 +2049,7 @@
74BF92192D395FE800B923E3 /* ACToastController.swift in Sources */,
1558BADE2D31AB6C00ECDEF8 /* SpotListViewController.swift in Sources */,
74BF92112D391FFE00B923E3 /* LocalVerificationViewController.swift in Sources */,
1576806E2DFA06A70079B255 /* SpotListSkeletonHeader.swift in Sources */,
74BF92122D391FFE00B923E3 /* LocalMapViewController.swift in Sources */,
D6E8168E2D6228F5001E4EBF /* WithdrawalViewController.swift in Sources */,
740968B72DEC3CA700AFF624 /* VerifiedAreasCollectionViewCell.swift in Sources */,
Expand Down Expand Up @@ -2389,6 +2397,14 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
15AD50962DF8725200166D9D /* XCRemoteSwiftPackageReference "SkeletonView" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Juanpe/SkeletonView.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.31.0;
};
};
746AA0302DAD0EE300E28B66 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
Expand Down Expand Up @@ -2464,6 +2480,11 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
15AD50972DF8725200166D9D /* SkeletonView */ = {
isa = XCSwiftPackageProductDependency;
package = 15AD50962DF8725200166D9D /* XCRemoteSwiftPackageReference "SkeletonView" */;
productName = SkeletonView;
};
746AA0332DAD0EE300E28B66 /* FirebaseCrashlytics */ = {
isa = XCSwiftPackageProductDependency;
package = 746AA0302DAD0EE300E28B66 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
Expand Down
23 changes: 23 additions & 0 deletions ACON-iOS/ACON-iOS/Global/Extensions/UIView+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import UIKit

import SkeletonView

extension UIView {

// MARK: - UIView 여러 개 한 번에 addSubview
Expand Down Expand Up @@ -150,3 +152,24 @@ extension UIView {
}

}


// MARK: - Skeleton 설정

extension UIView {

func startACSkeletonAnimation(direction: GradientDirection = .topLeftBottomRight,
duration: CFTimeInterval = 1.5,
autoreverses: Bool = true) {
DispatchQueue.main.async {
let diagonalAnimation = SkeletonAnimationBuilder().makeSlidingAnimation(withDirection: direction, duration: duration, autoreverses: autoreverses)

self.showAnimatedGradientSkeleton(
usingGradient: .init(colors: [.acWhite.withAlphaComponent(0.3),
.acWhite.withAlphaComponent(0.1)]),
animation: diagonalAnimation
)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import UIKit

import SkeletonView

final class SavedSpotCollectionViewCell: BaseCollectionViewCell {

// MARK: - UI Properties
Expand All @@ -19,7 +21,7 @@ final class SavedSpotCollectionViewCell: BaseCollectionViewCell {
override func setHierarchy() {
super.setHierarchy()

self.addSubviews(savedSpotView)
contentView.addSubviews(savedSpotView)
}

override func setLayout() {
Expand All @@ -37,6 +39,7 @@ final class SavedSpotCollectionViewCell: BaseCollectionViewCell {
$0.backgroundColor = .clear
$0.layer.cornerRadius = 8
$0.clipsToBounds = true
$0.isSkeletonable = true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,101 +7,122 @@

import UIKit

import SkeletonView

final class SavedSpotView: BaseView {

// MARK: - UI Properties

private let skeletonView: UIView = UIView()

private let gradientView: UIView = UIView()

private let spotImageView: UIImageView = UIImageView()

private let spotNameLabel: UILabel = UILabel()

private let preparingImageLabel: UILabel = UILabel()


private let spotNameSkeletonView = UIView()


// MARK: - Lifecycle

override func setHierarchy() {
super.setHierarchy()

self.addSubviews(skeletonView,
spotImageView,
gradientView,
spotNameLabel,
preparingImageLabel)
preparingImageLabel,
spotNameSkeletonView)
}

override func setLayout() {
super.setLayout()

skeletonView.snp.makeConstraints {
$0.edges.equalToSuperview()
}

gradientView.snp.makeConstraints {
$0.top.horizontalEdges.equalToSuperview()
$0.height.equalToSuperview().dividedBy(4)
}

spotImageView.snp.makeConstraints {
$0.edges.equalToSuperview()
}

spotNameLabel.snp.makeConstraints {
$0.top.equalToSuperview().inset(ScreenUtils.heightRatio*12)
$0.horizontalEdges.equalToSuperview().inset(20*ScreenUtils.widthRatio)
$0.top.horizontalEdges.equalToSuperview().inset(20 * ScreenUtils.heightRatio)
$0.height.equalTo(20)
}

preparingImageLabel.snp.makeConstraints {
$0.center.equalToSuperview()
}

spotNameSkeletonView.snp.makeConstraints {
$0.top.horizontalEdges.equalTo(spotNameLabel)
$0.height.equalTo(26)
}
}

override func setStyle() {
super.setStyle()

self.do {
$0.backgroundColor = .clear
$0.layer.cornerRadius = 8
$0.clipsToBounds = true
}

skeletonView.do {
$0.layer.cornerRadius = 8
}

spotImageView.do {
$0.clipsToBounds = true
$0.contentMode = .scaleAspectFill
}

preparingImageLabel.do {
$0.isHidden = true
$0.setLabel(text: "이미지 준비중", style: .c1R)
}

[self, skeletonView, spotNameSkeletonView].forEach {
$0.isSkeletonable = true
$0.skeletonCornerRadius = 8
}
}

override func layoutSubviews() {
super.layoutSubviews()

DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.gradientView.setGradient(
bottomColor: .gray900.withAlphaComponent(0)
)
}
}

}


// MARK: - Bind Data

extension SavedSpotView {

func bindData(_ data: SavedSpotModel) {
skeletonView.isHidden = false
startACSkeletonAnimation()
spotNameLabel.isHidden = true

preparingImageLabel.isHidden = true

spotNameLabel.setLabel(text: data.name.abbreviatedStringWithException(9), style: .t5SB)

if let imageURL = data.image {
Expand All @@ -111,16 +132,19 @@ extension SavedSpotView {
) { [weak self] result in
DispatchQueue.main.async {
self?.preparingImageLabel.isHidden = true
self?.skeletonView.isHidden = true
self?.endSkeletonAnimation()
}
}
} else {
spotImageView.image = .imgSpotNoImageBackground
preparingImageLabel.isHidden = false
skeletonView.isHidden = true

DispatchQueue.main.async { [weak self] in
self?.endSkeletonAnimation()
}
}
}

}


Expand All @@ -137,5 +161,12 @@ extension SavedSpotView {
$0.spotNameLabel.text = nil
}
}


private func endSkeletonAnimation() {
hideSkeleton()

spotNameLabel.isHidden = false
[skeletonView, spotNameSkeletonView].forEach { $0.isHidden = true}
}

}
Loading