top bar test
This commit is contained in:
parent
975802dc88
commit
2052492912
@ -4,17 +4,20 @@ import UIKit
|
|||||||
struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
||||||
var content: Content
|
var content: Content
|
||||||
var onRefresh: () -> Void
|
var onRefresh: () -> Void
|
||||||
|
var onScroll: ((CGPoint) -> Void)?
|
||||||
var isRefreshing: Binding<Bool>
|
var isRefreshing: Binding<Bool>
|
||||||
|
|
||||||
init(isRefreshing: Binding<Bool>, onRefresh: @escaping () -> Void, @ViewBuilder content: () -> Content) {
|
init(isRefreshing: Binding<Bool>, onRefresh: @escaping () -> Void, onScroll: ((CGPoint) -> Void)? = nil, @ViewBuilder content: () -> Content) {
|
||||||
self.content = content()
|
self.content = content()
|
||||||
self.onRefresh = onRefresh
|
self.onRefresh = onRefresh
|
||||||
|
self.onScroll = onScroll
|
||||||
self.isRefreshing = isRefreshing
|
self.isRefreshing = isRefreshing
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeUIView(context: Context) -> UIScrollView {
|
func makeUIView(context: Context) -> UIScrollView {
|
||||||
let scrollView = UIScrollView()
|
let scrollView = UIScrollView()
|
||||||
scrollView.delaysContentTouches = false
|
scrollView.delaysContentTouches = false
|
||||||
|
scrollView.delegate = context.coordinator
|
||||||
|
|
||||||
let refreshControl = UIRefreshControl()
|
let refreshControl = UIRefreshControl()
|
||||||
refreshControl.addTarget(context.coordinator, action: #selector(Coordinator.handleRefresh), for: .valueChanged)
|
refreshControl.addTarget(context.coordinator, action: #selector(Coordinator.handleRefresh), for: .valueChanged)
|
||||||
@ -41,7 +44,6 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
|||||||
if isRefreshing.wrappedValue {
|
if isRefreshing.wrappedValue {
|
||||||
uiView.refreshControl?.beginRefreshing()
|
uiView.refreshControl?.beginRefreshing()
|
||||||
} else {
|
} else {
|
||||||
// Отложенное завершение, чтобы избежать цикла обновлений
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
uiView.refreshControl?.endRefreshing()
|
uiView.refreshControl?.endRefreshing()
|
||||||
}
|
}
|
||||||
@ -54,7 +56,7 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
|||||||
Coordinator(self)
|
Coordinator(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Coordinator: NSObject {
|
class Coordinator: NSObject, UIScrollViewDelegate {
|
||||||
var parent: RefreshableScrollView
|
var parent: RefreshableScrollView
|
||||||
var hostingController: UIHostingController<Content>?
|
var hostingController: UIHostingController<Content>?
|
||||||
|
|
||||||
@ -65,5 +67,9 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
|||||||
@objc func handleRefresh() {
|
@objc func handleRefresh() {
|
||||||
parent.onRefresh()
|
parent.onRefresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
parent.onScroll?(scrollView.contentOffset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,14 +5,18 @@ struct HomeTab: View {
|
|||||||
@State private var isLoading = true
|
@State private var isLoading = true
|
||||||
@State private var isRefreshing = false
|
@State private var isRefreshing = false
|
||||||
|
|
||||||
|
var onScroll: ((CGPoint) -> Void)?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
if isLoading {
|
if isLoading {
|
||||||
ProgressView("Загрузка ленты...")
|
ProgressView("Загрузка ленты...")
|
||||||
} else {
|
} else {
|
||||||
RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: {
|
RefreshableScrollView(
|
||||||
fetchData()
|
isRefreshing: $isRefreshing,
|
||||||
}) {
|
onRefresh: { fetchData() },
|
||||||
|
onScroll: onScroll
|
||||||
|
) {
|
||||||
LazyVStack(spacing: 24) {
|
LazyVStack(spacing: 24) {
|
||||||
ForEach(posts) { post in
|
ForEach(posts) { post in
|
||||||
PostDetailView(post: post)
|
PostDetailView(post: post)
|
||||||
|
|||||||
@ -11,11 +11,15 @@ struct MainView: View {
|
|||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
@State private var selectedTab: Int = 0
|
@State private var selectedTab: Int = 0
|
||||||
|
|
||||||
// Состояния для TopBarView, когда активна вкладка Profile
|
// Состояния для TopBarView
|
||||||
@State private var selectedAccount = "@user1"
|
@State private var selectedAccount = "@user1"
|
||||||
@State private var accounts = ["@user1", "@user2", "@user3"]
|
@State private var accounts = ["@user1", "@user2", "@user3"]
|
||||||
@State private var sheetType: ProfileTab.SheetType? = nil
|
@State private var sheetType: ProfileTab.SheetType? = nil
|
||||||
|
|
||||||
|
// Состояния для скрытия TopBar
|
||||||
|
@State private var topBarVisible = true
|
||||||
|
@State private var lastScrollY: CGFloat = 0
|
||||||
|
|
||||||
private var tabTitle: String {
|
private var tabTitle: String {
|
||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
case 0:
|
case 0:
|
||||||
@ -32,7 +36,7 @@ struct MainView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView { // NavigationView нужен здесь для NavigationLink в TopBarView
|
NavigationView {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
TopBarView(
|
TopBarView(
|
||||||
title: tabTitle,
|
title: tabTitle,
|
||||||
@ -41,38 +45,38 @@ struct MainView: View {
|
|||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
viewModel: viewModel
|
viewModel: viewModel
|
||||||
)
|
)
|
||||||
|
.offset(y: topBarVisible ? 0 : -100)
|
||||||
|
.animation(.spring(response: 0.4, dampingFraction: 0.8), value: topBarVisible)
|
||||||
|
|
||||||
ZStack {
|
ZStack {
|
||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
case 0:
|
case 0:
|
||||||
HomeTab()
|
HomeTab(onScroll: handleScroll)
|
||||||
case 1:
|
case 1:
|
||||||
SearchTab()
|
SearchTab() // SearchTab не имеет скролла, поэтому onScroll не нужен
|
||||||
case 2:
|
case 2:
|
||||||
ChatsTab()
|
ChatsTab() // ChatsTab тоже
|
||||||
case 3:
|
case 3:
|
||||||
// Передаем состояния в ProfileTab
|
|
||||||
ProfileTab(
|
ProfileTab(
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
sheetType: $sheetType,
|
sheetType: $sheetType,
|
||||||
selectedAccount: $selectedAccount,
|
selectedAccount: $selectedAccount,
|
||||||
accounts: $accounts
|
accounts: $accounts,
|
||||||
|
onScroll: handleScroll
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
HomeTab()
|
HomeTab(onScroll: handleScroll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
|
||||||
CustomTabBar(selectedTab: $selectedTab) {
|
CustomTabBar(selectedTab: $selectedTab) {
|
||||||
// Действие для кнопки "Создать"
|
|
||||||
print("Create button tapped")
|
print("Create button tapped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoresSafeArea(edges: .bottom)
|
.ignoresSafeArea(edges: .bottom)
|
||||||
.navigationBarHidden(true) // Скрываем стандартный NavigationBar
|
.navigationBarHidden(true)
|
||||||
.sheet(item: $sheetType) { type in
|
.sheet(item: $sheetType) { type in
|
||||||
// Обработка sheet, перенесенная из ProfileTab
|
|
||||||
switch type {
|
switch type {
|
||||||
case .accountShare:
|
case .accountShare:
|
||||||
AccountShareSheet(
|
AccountShareSheet(
|
||||||
@ -86,7 +90,26 @@ struct MainView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle()) // Важно для корректной работы
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleScroll(offset: CGPoint) {
|
||||||
|
let currentY = offset.y
|
||||||
|
|
||||||
|
// Показываем бар, если скроллим вверх или дошли до верха
|
||||||
|
if currentY < lastScrollY || currentY <= 0 {
|
||||||
|
if !topBarVisible {
|
||||||
|
topBarVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Скрываем, если скроллим вниз и отступили от верха
|
||||||
|
else if currentY > lastScrollY && currentY > 50 {
|
||||||
|
if topBarVisible {
|
||||||
|
topBarVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScrollY = currentY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,8 @@ struct ProfileTab: View {
|
|||||||
@Binding var selectedAccount: String
|
@Binding var selectedAccount: String
|
||||||
@Binding var accounts: [String]
|
@Binding var accounts: [String]
|
||||||
|
|
||||||
|
var onScroll: ((CGPoint) -> Void)?
|
||||||
|
|
||||||
let followers = ["@alice", "@bob", "@charlie"]
|
let followers = ["@alice", "@bob", "@charlie"]
|
||||||
let following = ["@dev", "@design", "@ios"]
|
let following = ["@dev", "@design", "@ios"]
|
||||||
|
|
||||||
@ -47,9 +49,11 @@ struct ProfileTab: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: {
|
RefreshableScrollView(
|
||||||
fetchData()
|
isRefreshing: $isRefreshing,
|
||||||
}) {
|
onRefresh: { fetchData() },
|
||||||
|
onScroll: onScroll
|
||||||
|
) {
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
header
|
header
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user