feat(series-all-by-genre): 시리즈 전체보기 장르별 탭 - 장르, 시리즈 UI 추가
This commit is contained in:
@@ -14,6 +14,47 @@ struct SeriesMainByGenreView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
|
if !viewModel.genreList.isEmpty {
|
||||||
|
SeriesMainGenreView(
|
||||||
|
genreList: viewModel.genreList,
|
||||||
|
selectGenre: viewModel.onTapGenre,
|
||||||
|
selectedGenre: $viewModel.selectedGenre
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
|
let horizontalPadding: CGFloat = 24
|
||||||
|
let gridSpacing: CGFloat = 16
|
||||||
|
let width = (screenSize().width - (horizontalPadding * 2) - gridSpacing) / 2
|
||||||
|
|
||||||
|
LazyVGrid(
|
||||||
|
columns: Array(
|
||||||
|
repeating: GridItem(
|
||||||
|
.flexible(),
|
||||||
|
spacing: gridSpacing,
|
||||||
|
alignment: .topLeading
|
||||||
|
),
|
||||||
|
count: 2
|
||||||
|
),
|
||||||
|
alignment: .leading,
|
||||||
|
spacing: gridSpacing
|
||||||
|
) {
|
||||||
|
ForEach(viewModel.seriesList.indices, id: \.self) { index in
|
||||||
|
let item = viewModel.seriesList[index]
|
||||||
|
NavigationLink {
|
||||||
|
SeriesDetailView(seriesId: item.seriesId)
|
||||||
|
} label: {
|
||||||
|
SeriesMainItemView(item: item, width: width, height: width * 227 / 160)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onAppear {
|
||||||
|
if index == viewModel.seriesList.count - 1 {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, horizontalPadding)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) {
|
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) {
|
||||||
HStack {
|
HStack {
|
||||||
|
|||||||
@@ -19,14 +19,16 @@ final class SeriesMainByGenreViewModel: ObservableObject {
|
|||||||
@Published var genreList: [GetSeriesGenreListResponse] = []
|
@Published var genreList: [GetSeriesGenreListResponse] = []
|
||||||
@Published var seriesList: [SeriesListItem] = []
|
@Published var seriesList: [SeriesListItem] = []
|
||||||
|
|
||||||
|
@Published var selectedGenre: GetSeriesGenreListResponse = GetSeriesGenreListResponse(id: 0, genre: "")
|
||||||
|
|
||||||
private var page = 1
|
private var page = 1
|
||||||
private var isLast = false
|
private var isLast = false
|
||||||
private let pageSize = 20
|
private let pageSize = 20
|
||||||
|
|
||||||
func onTapGenre(genreId: Int) {
|
func onTapGenre() {
|
||||||
page = 1
|
page = 1
|
||||||
isLast = false
|
isLast = false
|
||||||
getSeriesListByGenre(genreId: genreId)
|
getSeriesListByGenre()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGenreList() {
|
func getGenreList() {
|
||||||
@@ -54,7 +56,8 @@ final class SeriesMainByGenreViewModel: ObservableObject {
|
|||||||
|
|
||||||
if let data = decoded.data, decoded.success {
|
if let data = decoded.data, decoded.success {
|
||||||
self.genreList = data
|
self.genreList = data
|
||||||
self.getSeriesListByGenre(genreId: genreList[0].id)
|
self.selectedGenre = data[0]
|
||||||
|
self.getSeriesListByGenre()
|
||||||
} else {
|
} else {
|
||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self.errorMessage = message
|
self.errorMessage = message
|
||||||
@@ -72,11 +75,11 @@ final class SeriesMainByGenreViewModel: ObservableObject {
|
|||||||
.store(in: &subscription)
|
.store(in: &subscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSeriesListByGenre(genreId: Int) {
|
func getSeriesListByGenre() {
|
||||||
if !isLast && !isLoading {
|
if !isLast && !isLoading {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
||||||
repository.getSeriesListByGenre(genreId: genreId, page: page, size: pageSize)
|
repository.getSeriesListByGenre(genreId: selectedGenre.id, page: page, size: pageSize)
|
||||||
.sink { result in
|
.sink { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .finished:
|
case .finished:
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// SeriesMainGenreView.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 11/15/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SeriesMainGenreView: View {
|
||||||
|
let genreList: [GetSeriesGenreListResponse]
|
||||||
|
let selectGenre: () -> Void
|
||||||
|
|
||||||
|
@Binding var selectedGenre: GetSeriesGenreListResponse
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
HStack(alignment: .top, spacing: 16) {
|
||||||
|
ForEach(0..<genreList.count, id: \.self) { index in
|
||||||
|
let genre = genreList[index]
|
||||||
|
Text(genre.genre)
|
||||||
|
.font(
|
||||||
|
.custom(
|
||||||
|
selectedGenre.genre == genre.genre ? Font.preBold.rawValue : Font.preRegular.rawValue,
|
||||||
|
size: 16
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
.padding(.vertical, 12)
|
||||||
|
.background(
|
||||||
|
selectedGenre.genre == genre.genre ? Color.button : Color(hex: "263238")
|
||||||
|
)
|
||||||
|
.cornerRadius(999)
|
||||||
|
.onTapGesture {
|
||||||
|
if selectedGenre.genre != genre.genre {
|
||||||
|
selectedGenre = genre
|
||||||
|
selectGenre()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SeriesMainGenreView(
|
||||||
|
genreList: [
|
||||||
|
GetSeriesGenreListResponse(id: 1, genre: "test"),
|
||||||
|
GetSeriesGenreListResponse(id: 2, genre: "test2"),
|
||||||
|
GetSeriesGenreListResponse(id: 3, genre: "test3")
|
||||||
|
],
|
||||||
|
selectGenre: { },
|
||||||
|
selectedGenre: .constant(GetSeriesGenreListResponse(id: 1, genre: "test"))
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user