From f19b04dc0e9a37546f8a8b43ebaf22c922c5a3eb Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Fri, 3 Jul 2026 01:34:40 +0900 Subject: [PATCH] =?UTF-8?q?feat(creator):=20=EC=B1=84=EB=84=90=20=ED=83=AD?= =?UTF-8?q?=20=EA=B3=A0=EC=A0=95=EC=9D=84=20=EC=97=B0=EA=B2=B0=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/CreatorChannelTitleBar.swift | 6 +- .../CreatorChannel/CreatorChannelView.swift | 67 ++++++++++++++++--- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift b/SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift index 5348dc5b..854a133b 100644 --- a/SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift +++ b/SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift @@ -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) { diff --git a/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift b/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift index 8adba500..80e58711 100644 --- a/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift +++ b/SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift @@ -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)