Skip to content

Commit

Permalink
Merge branch 'feature/밈디테일수정' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
jongnan committed Oct 3, 2024
2 parents 62cdbd0 + 2ba3b84 commit a59086a
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 78 deletions.
24 changes: 21 additions & 3 deletions Projects/Core/DesignSystem/Sources/Extension/View+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,23 @@ public extension View {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
}
}

public extension View {

@ViewBuilder
func onReadSize(_ perform: @escaping (CGSize) -> Void) -> some View {
self.customBackground {
GeometryReader { geometryProxy in
Color.clear
.preference(key: SizePreferenceKey.self, value: geometryProxy.size)
}
}
.onPreferenceChange(SizePreferenceKey.self, perform: perform)
}

@ViewBuilder
func customBackground<V: View>(alignment: Alignment = .center, @ViewBuilder content: () -> V) -> some View {
self.background(alignment: alignment, content: content)
}

func popup(
isActive: Binding<Bool>,
image: SwiftUI.Image?,
Expand All @@ -55,4 +69,8 @@ public extension View {
}
}

struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) { }
}

Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public struct MemeImageView: View {
.cacheMemoryOnly()
.fade(duration: 0.25)
.frame(maxWidth: .infinity)
.aspectRatio(0.9375, contentMode: .fit)
.cornerRadius(10)
.aspectRatio(contentMode: .fit)
}

var skeletonView: some View {
Expand Down
89 changes: 72 additions & 17 deletions Projects/Features/MemeDetail/Sources/MemeDetailCardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,70 @@ struct MemeDetailCardView: View {
@State var playbackMode: LottiePlaybackMode = .paused(at: .progress(100))

private let reactionButtonTapped: (() -> Void)?
private let isShortCard: Bool

// MARK: - Initializers

init(
meme: Binding<MemeDetail>,
isShortCard: Bool,
reactionButtonTapped: (() -> Void)?
) {
self._meme = meme
self.reactionButtonTapped = reactionButtonTapped
self.isShortCard = isShortCard
}

// MARK: - UI

var body: some View {
VStack(alignment: .center, spacing: 0) {

MemeImageView(imageUrlString: meme.imageUrlString)
.padding(.bottom, 25)
Rectangle()
.frame(width: 330, height: 352)
.overlay {
ZStack {
MemeImageView(imageUrlString: meme.imageUrlString)

if isShortCard {
shortCardGradation

VStack(spacing: 0) {
Spacer()

infoView
}
}
}
}
.cornerRadius(10)
.padding(.bottom, isShortCard ? 0 : 25)

if(!isShortCard) {
infoView
}
}
.frame(maxWidth: 330)
.padding(.horizontal, /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
.padding(.vertical, 12.5)
.background(Color.Background.white)
.cornerRadius(20)
.overlay(
RoundedRectangle(cornerRadius: 20)
.inset(by: 1)
.stroke(.black, lineWidth: 2)
.frame(maxWidth: 350)
)
}

// MARK: - Methods

var infoView: some View {
VStack(spacing: 0) {
titleLabel
.padding(.bottom, 5)

HashTagView(keywords: meme.keywords)
HashTagView(keywords: meme.keywords, isShortCard: isShortCard)
.padding(.bottom, 11)
.onTapGesture {
PPACAnalytics.shared
Expand All @@ -71,33 +112,43 @@ struct MemeDetailCardView: View {
}
.offset(y: -50)
})
.padding(.bottom, 20)
.padding(.bottom, 10)
.padding(.horizontal, 10)
}
.padding(10)
.background(Color.Background.white)
.cornerRadius(20)
.overlay(
RoundedRectangle(cornerRadius: 20)
.inset(by: 1)
.stroke(.black, lineWidth: 2)
)
}

// MARK: - Methods

var titleLabel: some View {
Text(meme.title)
.font(Font.Heading.Large.semiBold)
.multilineTextAlignment(.center)
.foregroundColor(Color.Text.primary)
.foregroundColor(
isShortCard ? Color.Text.inverse : Color.Text.primary
)
.frame(maxWidth: .infinity, alignment: .center)
}

var subtitleLabel: some View {
Text("출처: \(self.meme.source)")
.font(Font.Body.Xsmall.medium)
.lineLimit(1)
.foregroundColor(Color.Icon.assistive)
.foregroundColor(
isShortCard ? Color.Text.assistive : Color.Icon.assistive
)
}

var shortCardGradation: some View {
Rectangle()
.opacity(0)
.background(
LinearGradient(
colors: [
ResourceKitAsset.PrimaryColor.neutral70.swiftUIColor.opacity(0),
ResourceKitAsset.PrimaryColor.neutral70.swiftUIColor
],
startPoint: .top,
endPoint: .bottom
)
)
}

private func handleReactionTapped() {
Expand All @@ -110,7 +161,11 @@ struct MemeDetailCardView: View {
@State var mock: MemeDetail = .mock

return VStack {
MemeDetailCardView(meme: $mock, reactionButtonTapped: nil)
MemeDetailCardView(
meme: $mock,
isShortCard: false,
reactionButtonTapped: nil
)
}
.background(.red)
}
126 changes: 95 additions & 31 deletions Projects/Features/MemeDetail/Sources/MemeDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@ import DesignSystem

import Kingfisher
import PPACAnalytics
import PPACDomain
import PPACData
import PPACNetwork

public struct MemeDetailView: View {

// MARK: - Properties

@ObservedObject private var viewModel: MemeDetailViewModel

@State private var totalHeight: CGFloat = 0
@State private var memeCardHeight: CGFloat = 0
@State private var tabBarHeight: CGFloat = 0

private var isShortCard: Bool {
memeCardHeight + tabBarHeight > totalHeight - 30
}

// MARK: - Initializers

public init(viewModel: MemeDetailViewModel) {
Expand All @@ -30,27 +41,71 @@ public struct MemeDetailView: View {
// MARK: - UI

public var body: some View {
Spacer()

MemeDetailCardView(meme: $viewModel.state.meme) {
viewModel.dispatch(type: .likeButtonTapped)
ZStack {
VStack(spacing: 0) {
Spacer()

MemeDetailCardView(
meme: $viewModel.state.meme,
isShortCard: totalHeight == 0 ? false : isShortCard
) {
viewModel.dispatch(type: .likeButtonTapped)
}
.padding(.top, 40)
.onReadSize { size in
if(memeCardHeight == 0) {
memeCardHeight = size.height
}
}

Spacer()

// 가짜 탭뷰
Rectangle()
.frame(height: 64)
.foregroundColor(.black.opacity(0))
.clipShape(
.rect(
topLeadingRadius: 30,
topTrailingRadius: 30
)
)
}

VStack(spacing: 0) {
Spacer()

EmptyView()
.memeDetailTabBar(
isFarmemed: $viewModel.state.meme.isFarmemed
) { tab in
tabBarTap(tab)
}
.frame(maxHeight: 64)
.onReadSize { size in
if(tabBarHeight == 0) {
tabBarHeight = size.height
}
}
}
}
.padding(.horizontal, 24)
.memeDetailTabBar(isFarmemed: $viewModel.state.meme.isFarmemed) { tab in
tabBarTap(tab)
.onReadSize { size in
if(totalHeight == 0) {
totalHeight = size.height
}
}
.background(
KFImage(URL(string: viewModel.state.meme.imageUrlString))
.resizable()
.loadDiskFileSynchronously()
.cacheMemoryOnly()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.clipped()
.opacity(0.4) // Image Opacity: 40%
.blur(radius: 50) // Layer Blur: 50
.overlay(Color.white.opacity(0.3)) // White Dim: #fff, Opacity: 30%
.edgesIgnoringSafeArea(.top)
KFImage(URL(string: viewModel.state.meme.imageUrlString))
.resizable()
.loadDiskFileSynchronously()
.cacheMemoryOnly()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.opacity(0.4) // Image Opacity: 40%
.blur(radius: 50) // Layer Blur: 50
.overlay(Color.white.opacity(0.3)) // White Dim: #fff, Opacity: 30%
.clipped()
.edgesIgnoringSafeArea(.top)
)
.onAppear {
viewModel.logMemeDetail(interaction: .view, event: .meme)
Expand All @@ -59,7 +114,7 @@ public struct MemeDetailView: View {
backHandler: { viewModel.dispatch(type: .naviBackButtonTapped) },
rightActionHandler: nil,
hasConfigureButton: false,
title: viewModel.state.meme.title
title: "밈 자세히 보기"
)
.popup(
isActive: $viewModel.state.isCopied,
Expand All @@ -71,8 +126,6 @@ public struct MemeDetailView: View {
image: viewModel.state.meme.isFarmemed ? ResourceKitAsset.Icon.copyFilled.swiftUIImage : nil,
text: viewModel.state.meme.isFarmemed ? "파밈 완료!" : "파밈을 취소했어요"
)

Spacer()
}

@MainActor
Expand All @@ -87,13 +140,24 @@ public struct MemeDetailView: View {
}
}
}
//
//#Preview {
// MemeDetailView(
// viewModel: MemeDetailViewModel(
// meme: .mock,
// router: nil,
// postLikeUseCase: MockPostLikeUseCase()
// )
// )
//}

#Preview {
let networkService = NetworkService()
let memeRepository = MemeRepositoryImpl(networkservice: networkService)

let bookmarkMemeUseCase = BookmarkMemeUseCaseImpl(repository: memeRepository)
let watchMemeUseCase = WatchMemeUseCaseImpl(repository: memeRepository)
let reactToMemeUseCase = ReactToMemeUseCaseImpl(repository: memeRepository)
let shareMemeUseCase = ShareMemeUseCaseImpl(repository: memeRepository)

return MemeDetailView(
viewModel: MemeDetailViewModel(
meme: .mock,
router: nil,
bookmarkMemeUseCase: bookmarkMemeUseCase,
shareMemeUseCase: shareMemeUseCase,
watchMemeUseCase: watchMemeUseCase,
reactToMemeUseCase:reactToMemeUseCase
)
)
}
11 changes: 9 additions & 2 deletions Projects/Features/MemeDetail/Sources/View/HashTagView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ public struct HashTagView: View {
// MARK: - Properties

private let keywords: [String]
private let isShortCard: Bool

// MARK: - Initializers

public init(keywords: [String]) {
public init(
keywords: [String],
isShortCard: Bool
) {
self.keywords = keywords
self.isShortCard = isShortCard
}

// MARK: - UI
Expand All @@ -36,7 +41,9 @@ public struct HashTagView: View {
func hashTag(title: String) -> some View {
Text("#\(title)")
.font(Font.Body.Large.medium)
.foregroundColor(Color.Text.tertiary)
.foregroundColor(
isShortCard ? Color.Text.disabled : Color.Text.tertiary
)
}

}
Loading

0 comments on commit a59086a

Please sign in to comment.