Skip to content

Commit

Permalink
feat: 검색 화면 구현 (#33)
Browse files Browse the repository at this point in the history
* feat: 검색 화면 구현

* refactor: 컴포넌트 분리

* fix: 파일 위치 수정
  • Loading branch information
lina0322 authored Jun 29, 2024
1 parent a555fb1 commit f7d9d37
Show file tree
Hide file tree
Showing 16 changed files with 588 additions and 19 deletions.
75 changes: 75 additions & 0 deletions Projects/Core/DesignSystem/Sources/BasicModal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// BasicModal.swift
// DesignSystem
//
// Created by 리나 on 2024/06/30.
//

import SwiftUI
import ResourceKit

// Thanks to 균
public struct BasicModal<Content: View>: View {
@Binding var isPresented: Bool
private let content: () -> Content
private var opacity: Double

public init(
isPresented: Binding<Bool>,
opacity: Double,
content: @escaping () -> Content
) {
self._isPresented = isPresented
self.opacity = opacity
self.content = content
}

public var body: some View {
GeometryReader { geometry in
ZStack {
if isPresented {
Color.black.opacity(opacity)
.onTapGesture {
self.isPresented.toggle()
}
.transition(.opacity)

content()
.padding(.horizontal, 40)
}
}
.edgesIgnoringSafeArea(.all)
}
}
}

extension View {
public func basicModal<Content: View>(
isPresented: Binding<Bool>,
opacity: Double,
@ViewBuilder content: @escaping () -> Content
) -> some View {
modifier(
BasicModalModifier(
content: {
BasicModal(
isPresented: isPresented,
opacity: opacity,
content: content
)
}
)
)
}
}

fileprivate struct BasicModalModifier<SheetContent>: ViewModifier where SheetContent: View {
var content: () -> BasicModal<SheetContent>

func body(content: Content) -> some View {
ZStack {
content
self.content()
}
}
}
87 changes: 87 additions & 0 deletions Projects/Core/DesignSystem/Sources/CategoryTagView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// CategoryTagView.swift
// DesignSystem
//
// Created by 리나 on 2024/06/29.
//

import SwiftUI
import ResourceKit

// thanks to NamS
public struct CategoryTagView: View {
@State public var categories: [String]

public init(categories: [String]) {
self.categories = categories
}

public var body: some View {
ScrollView {
CategoryTagLayout(verticalSpacing: 8, horizontalSpacing: 8) {
ForEach(categories, id: \.self) { category in
Text(category)
.font(Font.Body.Medium.medium)
.foregroundColor(Color.Text.primary)
.padding(.horizontal, 16)
.padding(.vertical, 9.5)
.background(
Capsule().foregroundStyle(Color.Background.assistive)
)
}
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
let cacheValue = categories
categories = []
categories = cacheValue
}
}
}
}

struct CategoryTagLayout: Layout {
var verticalSpacing: CGFloat = 0
var horizontalSpacing: CGFloat = 0

func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) -> CGSize {
CGSize(width: proposal.width ?? 0, height: cache.height)
}

func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) {
var sumX: CGFloat = bounds.minX
var sumY: CGFloat = bounds.minY

for index in subviews.indices {
let view = subviews[index]
let viewSize = view.sizeThatFits(.unspecified)
guard let proposalWidth = proposal.width else { continue }

if (sumX + viewSize.width > proposalWidth) {
sumX = bounds.minX
sumY += viewSize.height
sumY += verticalSpacing
}

let point = CGPoint(x: sumX, y: sumY)
view.place(at: point, anchor: .topLeading, proposal: proposal)
sumX += viewSize.width
sumX += horizontalSpacing
}

if let firstViewSize = subviews.first?.sizeThatFits(.unspecified) {
cache.height = sumY + firstViewSize.height
}
}

struct Cache {
var height: CGFloat
}

func makeCache(subviews: Subviews) -> Cache {
return Cache(height: 0)
}

func updateCache(_ cache: inout Cache, subviews: Subviews) { }
}
38 changes: 38 additions & 0 deletions Projects/Core/DesignSystem/Sources/HorizontalMimScrollView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// HorizontalMimScrollView.swift
// DesignSystem
//
// Created by 리나 on 2024/06/29.
//

import SwiftUI
import ResourceKit

public protocol HorizontalMimItemProtocol: Hashable { }

public protocol HorizontalMimItemViewProtocol: View {
associatedtype Item: HorizontalMimItemProtocol
init(item: Item)
}

public struct HorizontalMimScrollView<Item: HorizontalMimItemProtocol, ItemView: HorizontalMimItemViewProtocol>: View where ItemView.Item == Item {
@Binding public var items: [Item]

public init(items: Binding<[Item]>) {
self._items = items
}

public var body: some View {
ScrollView(.horizontal) {
LazyHStack(spacing: 10) {
ForEach(items, id: \.self) { item in
ItemView(item: item)
}
.listStyle(.plain)
}
}
.contentMargins(20)
.scrollIndicators(.hidden)
.frame(height: 90)
}
}
38 changes: 38 additions & 0 deletions Projects/Core/DesignSystem/Sources/MimCategoryView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// MimCategoryView.swift
// DesignSystem
//
// Created by 리나 on 2024/06/30.
//

import SwiftUI
import ResourceKit

public struct MimCategoryView: View {
public let title: String
public let categories: [String]

public init(title: String, categories: [String]) {
self.title = title
self.categories = categories
}

public var body: some View {
VStack(spacing: 0) {
HStack {
Text(title)
.font(Font.Body.Small.semiBold)
.foregroundColor(Color.Text.tertiary)

Spacer()
}
.padding(.top, 4)
.padding(.bottom, 16)
.padding(.horizontal, 20)

CategoryTagView(categories: categories)
.padding(.horizontal, 20)
.padding(.bottom, 20)
}
}
}
48 changes: 48 additions & 0 deletions Projects/Core/PPACModels/Sources/Search/HotKeyword.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// HotKeyword.swift
// PPACModels
//
// Created by 리나 on 2024/06/29.
//

import Foundation

public struct HotKeyword: Hashable {

// MARK: - Properties

public let title: String
public let imageUrlString: String

// MARK: - Initializers

public init(
title: String,
imageUrlString: String
) {
self.title = title
self.imageUrlString = imageUrlString
}
}

public extension HotKeyword {
static let mock1 = HotKeyword(
title: "띠용오오우어어우어엉?",
imageUrlString: "https://www.blockmedia.co.kr/wp-content/uploads/2024/05/%EB%8F%84%EC%A7%80%EC%BD%94%EC%9D%B8Doge-%EC%B9%B4%EB%B6%80%EC%88%98.png"
)

static let mock2 = HotKeyword(
title: "짜란다짜란다",
imageUrlString: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR_xO_sgszdvyVBp8xz4B82kHmyyO0SNZO-4A&s"
)

static let mock3 = HotKeyword(
title: "후회",
imageUrlString: "https://s3.ap-northeast-2.amazonaws.com/univ-careet/FileData/Picture/202310/3e7e0445-3812-4c06-bc75-a1bed40a3332_770x426.png"
)

static let mock4 = HotKeyword(
title: "무한도전",
imageUrlString: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQBnKb96Uc-894Fhgt_5xLsCvY0pSkCl0B4TA&s"
)
}
33 changes: 33 additions & 0 deletions Projects/Core/PPACModels/Sources/Search/MimCategory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// MimCategory.swift
// PPACModels
//
// Created by 리나 on 2024/06/29.
//

import Foundation

public struct MimCategory: Hashable {
public let title: String
public let categories: [String]

public init(title: String, categories: [String]) {
self.title = title
self.categories = categories
}
}

public extension MimCategory {
static let mock1 = MimCategory(
title: "감정",
categories: ["행복", "슬픈", "무념무상", "분노", "웃긴", "피곤", "절망", "현타", "당황"]
)
static let mock2 = MimCategory(
title: "상황",
categories: ["회사", "대학", "공부", "연애", "", "다이어트", "주식", "고민", "축하", "칭찬"]
)
static let mock3 = MimCategory(
title: "컨텐츠",
categories: ["동물", "무한도전", "캐릭터", "짱구", "문상훈", "검정고무신", "그 외"]
)
}
6 changes: 5 additions & 1 deletion Projects/Features/Search/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ let project = Project(
sources: "Sources/**",
resources: "Resources/**",
dependencies: [

.ResourceKit,
.Core.DesignSystem,
.Core.PPACModels,
.ThirdParty.Dependency,
.ThirdParty.Kingfisher,
]
)
]
Expand Down
40 changes: 40 additions & 0 deletions Projects/Features/Search/Sources/View/FakeSearchBar.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// FakeSearchBar.swift
// DesignSystem
//
// Created by 리나 on 2024/06/29.
//

import SwiftUI
import ResourceKit

struct FakeSearchBar: View {
let placeHolder: String

init(placeHolder: String) {
self.placeHolder = placeHolder
}

var body: some View {
fakeTextField
.frame(maxWidth: .infinity)
.frame(height: 44)
.background(Color.Background.assistive)
.clipShape(RoundedRectangle(cornerRadius: 10))
.padding(.horizontal, 20)
.padding(.vertical, 16)
}

private var fakeTextField: some View {
HStack(spacing: 12) {
ResourceKitAsset.Icon.search.swiftUIImage

Text(placeHolder)
.font(Font.Body.Large.medium)
.foregroundColor(Color.Text.tertiary)

Spacer()
}
.padding(.horizontal, 16)
}
}
Loading

0 comments on commit f7d9d37

Please sign in to comment.