feat(creator): 채널 탭 고정을 연결한다
This commit is contained in:
@@ -12,7 +12,7 @@ struct CreatorChannelTitleBar: View {
|
||||
let onTapMore: () -> Void
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 0) {
|
||||
HStack(spacing: SodaSpacing.s12) {
|
||||
Button(action: onTapBack) {
|
||||
Image("ic_new_bar_back")
|
||||
.resizable()
|
||||
@@ -20,6 +20,10 @@ struct CreatorChannelTitleBar: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Text("")
|
||||
.appFont(.heading2)
|
||||
|
||||
|
||||
Spacer(minLength: 0)
|
||||
|
||||
HStack(spacing: SodaSpacing.s14) {
|
||||
|
||||
@@ -5,6 +5,10 @@ struct CreatorChannelView: View {
|
||||
|
||||
@StateObject private var viewModel = CreatorChannelViewModel()
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var tabBarMinY: CGFloat = .greatestFiniteMagnitude
|
||||
|
||||
private let titleBarHeight: CGFloat = 56
|
||||
private let tabBarHeight: CGFloat = 52
|
||||
|
||||
init(creatorId: Int, viewModel: CreatorChannelViewModel = CreatorChannelViewModel()) {
|
||||
self.creatorId = creatorId
|
||||
@@ -12,23 +16,55 @@ struct CreatorChannelView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color.black.ignoresSafeArea()
|
||||
|
||||
GeometryReader { proxy in
|
||||
let titleBarBottomY = proxy.safeAreaInsets.top + titleBarHeight
|
||||
let backgroundProgress = clamp(1 - ((tabBarMinY - titleBarBottomY) / titleBarHeight))
|
||||
let isTabBarSticky = tabBarMinY <= titleBarBottomY
|
||||
|
||||
ZStack(alignment: .top) {
|
||||
Color.black.ignoresSafeArea()
|
||||
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
VStack(spacing: 0) {
|
||||
headerSection
|
||||
|
||||
tabBar
|
||||
.background(
|
||||
GeometryReader { geometry in
|
||||
let minY = geometry.frame(in: .global).minY
|
||||
Color.clear
|
||||
.onAppear {
|
||||
updateTabBarMinY(minY)
|
||||
}
|
||||
.onChange(of: minY) { value in
|
||||
updateTabBarMinY(value)
|
||||
}
|
||||
}
|
||||
)
|
||||
.opacity(isTabBarSticky ? 0 : 1)
|
||||
|
||||
selectedTabContent
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea(.container, edges: .vertical)
|
||||
|
||||
if isTabBarSticky {
|
||||
tabBar
|
||||
.frame(height: tabBarHeight)
|
||||
.offset(y: titleBarHeight)
|
||||
}
|
||||
|
||||
titleBar
|
||||
}
|
||||
|
||||
if viewModel.isLoading {
|
||||
LoadingView()
|
||||
VStack(spacing: 0) {
|
||||
Color.black
|
||||
.opacity(backgroundProgress)
|
||||
.frame(height: proxy.safeAreaInsets.top)
|
||||
titleBar(backgroundProgress: backgroundProgress)
|
||||
}
|
||||
.ignoresSafeArea(.container, edges: .top)
|
||||
|
||||
if viewModel.isLoading {
|
||||
LoadingView()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationBarBackButtonHidden(true)
|
||||
@@ -40,11 +76,11 @@ struct CreatorChannelView: View {
|
||||
.sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 1)
|
||||
}
|
||||
|
||||
private var titleBar: some View {
|
||||
private func titleBar(backgroundProgress: CGFloat) -> some View {
|
||||
CreatorChannelTitleBar(
|
||||
isFollow: viewModel.response?.creator.isFollow ?? false,
|
||||
isNotify: viewModel.response?.creator.isNotify ?? false,
|
||||
backgroundProgress: 0,
|
||||
backgroundProgress: backgroundProgress,
|
||||
onTapBack: {
|
||||
dismiss()
|
||||
},
|
||||
@@ -75,6 +111,17 @@ struct CreatorChannelView: View {
|
||||
CreatorChannelTabBar(selectedTab: $viewModel.selectedTab)
|
||||
}
|
||||
|
||||
private func clamp(_ value: CGFloat) -> CGFloat {
|
||||
max(0, min(value, 1))
|
||||
}
|
||||
|
||||
private func updateTabBarMinY(_ value: CGFloat) {
|
||||
guard tabBarMinY != value else { return }
|
||||
DispatchQueue.main.async {
|
||||
tabBarMinY = value
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var selectedTabContent: some View {
|
||||
CreatorChannelPlaceholderTabView(title: viewModel.selectedTab.title)
|
||||
|
||||
Reference in New Issue
Block a user