Skip to content

Commit

Permalink
feat: 스켈레톤 적용
Browse files Browse the repository at this point in the history
  • Loading branch information
jongnan committed Aug 11, 2024
1 parent 8f3b07f commit f21aed5
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) { }
}

extension View {
func recommendSkeleton(
isShow: Bool,
radius: CGFloat,
width: CGFloat? = nil,
height: CGFloat? = nil
) -> some View {
self.modifier(
SkeletonModifier(isShow: isShow, radius: radius, width: width, height: height)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// SkeletonModifier.swift
// Recommend
//
// Created by 김종윤 on 8/8/24.
//

import SwiftUI
import SkeletonUI

import ResourceKit

struct SkeletonModifier: ViewModifier {
let isShow: Bool
let radius: CGFloat
let width: CGFloat?
let height: CGFloat?

func body(content: Content) -> some View {
content
.skeleton(
with: isShow,
animation: .linear(duration: 2, delay: 0, speed: 1),
appearance: .gradient(
.linear,
color: Color.Skeleton.home,
background: Color.Skeleton.homeback,
radius: 1
),
shape: .rounded(.radius(radius))
)
.frame(width: width, height: height)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
//

import SwiftUI
import SkeletonUI

import ResourceKit

struct RecommendHeaderView: View {
Expand All @@ -15,22 +17,29 @@ struct RecommendHeaderView: View {
@Binding var recommendMemeSize: Int

public var body: some View {
let isNotLoading = userLevel == 0 || seenMemeCount == 0

VStack(spacing: 0) {
ResourceKitAsset.Icon.homeLogo.swiftUIImage
.resizable()
.frame(width: 212, height: 45, alignment: .center)
.padding(.bottom, 10)

recommendTitle
.padding(.bottom, 16)

recommendProgressBar(
seenMemeCount: self.seenMemeCount,
total: recommendMemeSize
)
.padding(.bottom, 8)

recommendText(getRecommendText())
if !isNotLoading {
recommendProgressBar(
seenMemeCount: self.seenMemeCount,
total: recommendMemeSize
)

recommendText(getRecommendText())
} else {
EmptyView()
.recommendSkeleton(isShow: isNotLoading, radius: 4, width: 200, height: 16)
.padding(.top, 20)
.padding(.bottom, 37)
}
}
}

Expand All @@ -41,7 +50,7 @@ struct RecommendHeaderView: View {
(1...(recommendMemeSize - 1)).contains(seenMemeCount)
{
"밈 보고 레벨 포인트 받아요!"
} else if userLevel == 2 &&
} else if userLevel == 2 &&
(1...(recommendMemeSize - 1)).contains(seenMemeCount)
{
"추천 밈 둘러보세요!"
Expand All @@ -54,7 +63,7 @@ struct RecommendHeaderView: View {
private var recommendTitle : some View {
Text("이번 주 이 밈 어때!")
.font(Font.Heading.Large.semiBold)
.padding(.bottom, 8)
.padding(.bottom, 16)
}

private func recommendProgressBar(
Expand Down Expand Up @@ -83,10 +92,12 @@ private func recommendText(_ text: String) -> some View {
Text(text)
.font(Font.Body.Medium.medium)
.foregroundStyle(Color.Text.secondary)
.padding(.top, 8)
.padding(.bottom, 32)
}

#Preview {
@State var userLevel: Int = 1
@State var userLevel: Int = 0
@State var seenMemeCount: Int = 5
@State var recommendMemeSize: Int = 5

Expand All @@ -95,4 +106,16 @@ private func recommendText(_ text: String) -> some View {
seenMemeCount: $seenMemeCount,
recommendMemeSize: $recommendMemeSize
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(
LinearGradient(
colors: [
Color.Background.brandassistive,
Color.Background.brandsubassistive
],
startPoint: .top,
endPoint: .bottom
)
)
.edgesIgnoringSafeArea(.bottom)
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ struct RecommendMemeButtonView : View {
}
}
.frame(maxWidth: .infinity)
.frame(height: 110, alignment: .center)
.padding(.vertical, 30)
.background(
LinearGradient(
colors: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct RecommendMemeImagesView: View {

@State var value: CGFloat = 0

@State var imageStatusList: [String : Bool] = [:]

var memes: [MemeDetail]
var isTagHidden: Bool = false

Expand All @@ -30,57 +32,70 @@ struct RecommendMemeImagesView: View {
ForEach(memes, id: \.self) { meme in
MemeImageView(
imageUrl: meme.imageUrlString,
isDimmed: meme.id != currentMeme?.id
isDimmed: meme.id != currentMeme?.id,
isLoadingImage: binding(for: meme.id)
)
.scrollTransition { content, phase in
content
.offset(x: phase.value * -3)
.scaleEffect(phase.isIdentity ? 1 : 0.9)
.blur(radius: phase.isIdentity ? 0 : 1)
}
.animation(.smooth, value: meme)
}
}

// Border
HStack(spacing: 0) {
ForEach(memes, id: \.self) { meme in
RoundedRectangle(cornerRadius: 20)
.inset(by: 1)
.stroke(Color.Border.primary, lineWidth: 2)
.scrollTransition { content, phase in
content
.offset(x: phase.value * -3)
.scaleEffect(phase.isIdentity ? 1 : 0.905)
}
.animation(.smooth, value: meme)
MemeImageBorderView(
isLoadingImage: binding(for: meme.id)
)
.animation(.smooth, value: meme)
}
}
}
.frame(height: 310)
.scrollTargetLayout()
.recommendSkeleton(
isShow: memes.isEmpty,
radius: 20,
width: memes.isEmpty ? 270 : .infinity,
height: 310
)
}
.scrollIndicators(.never)
.scrollTargetBehavior(.viewAligned)
.scrollPosition(id: $currentMeme)
.contentMargins(.horizontal, 60.0)
.padding(.top, 36)
.padding(.bottom, 20)

if let currentMeme, isTagHidden == false {
HashTagView(keywords: currentMeme.keywords)
} else {
EmptyView()
.recommendSkeleton(isShow: true, radius: 4, width: 200, height: 16)
}
}
.onAppear {
currentMeme = memes.first
}
.onChange(of: memes) { _, value in
let current = value.first(where: {
$0.id == currentMeme?.id
})
currentMeme = current
if let currentMeme {
let current = value.first(where: {
$0.id == currentMeme.id
})
self.currentMeme = current

} else {
self.currentMeme = memes.first
memes.forEach { meme in
imageStatusList[meme.id] = false
}
}
}
}

func binding(for key: String) -> Binding<Bool> {
return Binding(
get: {
return self.imageStatusList[key] ?? false
},
set: {
self.imageStatusList[key] = $0
}
)
}
}

#Preview {
Expand Down Expand Up @@ -157,4 +172,16 @@ struct RecommendMemeImagesView: View {
],
isTagHidden: false
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(
LinearGradient(
colors: [
Color.Background.brandassistive,
Color.Background.brandsubassistive
],
startPoint: .top,
endPoint: .bottom
)
)
.edgesIgnoringSafeArea(.bottom)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public struct RecommendView: View {
@State private var memeImageHeight: CGFloat = 0
@State private var buttonViewHeight: CGFloat = 0

var isOverlapView: Bool {
memeImageHeight + buttonViewHeight > memeContentsHeight + 30
}

@State private var currentMeme: MemeDetail?
@State var isActiveCopyPopup: Bool = false
@State var isFarmemed: Bool = false
Expand All @@ -41,9 +45,7 @@ public struct RecommendView: View {
VStack(spacing: 0) {
Spacer()

if viewModel.state.recommendMemeSize > 0 &&
!viewModel.state.isSuccessFetch
{
if viewModel.state.recommendMemeSize > 0 && !viewModel.state.isSuccessFetch {
ProgressView()
.frame(width: 30, height: 30, alignment: .center)
.padding(.bottom, 20)
Expand All @@ -56,18 +58,15 @@ public struct RecommendView: View {
)

ZStack {
let isOverlapView = memeImageHeight + buttonViewHeight > memeContentsHeight + 30
VStack(spacing: 0) {

if viewModel.state.recommendMemes.count > 0 {
RecommendMemeImagesView(
currentMeme: $currentMeme,
memes: viewModel.state.recommendMemes,
isTagHidden: isOverlapView
)
.onReadSize { size in
memeImageHeight = size.height
}
RecommendMemeImagesView(
currentMeme: $currentMeme,
memes: viewModel.state.recommendMemes,
isTagHidden: isOverlapView
)
.onReadSize { size in
memeImageHeight = size.height
}

Spacer()
Expand All @@ -77,24 +76,25 @@ public struct RecommendView: View {
VStack(spacing: 0) {
Spacer()

RecommendMemeButtonView(
isReaction: currentMeme?.isReaction ?? false,
reactionCnt: currentMeme?.reaction ?? 0,
isFarmemed: currentMeme?.isFarmemed ?? false,
isOverlapView: isOverlapView,
reactionButtonTapped: reactionButtonTap,
copyButtonTapped: copyButtonTap,
shareButtonTapped : shareButtonTap,
saveButtonTapped : saveButtonTap
)
.onReadSize { size in
print(size.height)
buttonViewHeight = size.height
if let currentMeme {
RecommendMemeButtonView(
isReaction: currentMeme.isReaction,
reactionCnt: currentMeme.reaction,
isFarmemed: currentMeme.isFarmemed,
isOverlapView: isOverlapView,
reactionButtonTapped: reactionButtonTap,
copyButtonTapped: copyButtonTap,
shareButtonTapped : shareButtonTap,
saveButtonTapped : saveButtonTap
)
.onReadSize { size in
buttonViewHeight = size.height
}
}
}
.zIndex(2)
}
.frame(maxHeight: 490)
.frame(maxHeight: 457)
.onReadSize { size in
memeContentsHeight = size.height
}
Expand Down Expand Up @@ -153,7 +153,9 @@ public struct RecommendView: View {
if value.translation.height < 0 { return }

withAnimation(.spring()) {
currentOffsetY = value.translation.height > 180 ? 180 : value.translation.height
currentOffsetY = value.translation.height > 180
? 180
: value.translation.height
}
})
.onEnded({ value in
Expand Down
Loading

0 comments on commit f21aed5

Please sign in to comment.