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