refresh profile
This commit is contained in:
parent
8fd955d3cb
commit
a716b296d5
@ -3,10 +3,10 @@ import UIKit
|
|||||||
|
|
||||||
struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
||||||
var content: Content
|
var content: Content
|
||||||
var onRefresh: (UIRefreshControl) -> Void
|
var onRefresh: () -> Void
|
||||||
var isRefreshing: Binding<Bool>
|
var isRefreshing: Binding<Bool>
|
||||||
|
|
||||||
init(isRefreshing: Binding<Bool>, onRefresh: @escaping (UIRefreshControl) -> Void, @ViewBuilder content: () -> Content) {
|
init(isRefreshing: Binding<Bool>, onRefresh: @escaping () -> Void, @ViewBuilder content: () -> Content) {
|
||||||
self.content = content()
|
self.content = content()
|
||||||
self.onRefresh = onRefresh
|
self.onRefresh = onRefresh
|
||||||
self.isRefreshing = isRefreshing
|
self.isRefreshing = isRefreshing
|
||||||
@ -15,17 +15,14 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
|||||||
func makeUIView(context: Context) -> UIScrollView {
|
func makeUIView(context: Context) -> UIScrollView {
|
||||||
let scrollView = UIScrollView()
|
let scrollView = UIScrollView()
|
||||||
|
|
||||||
// Создаем UIRefreshControl и добавляем его
|
|
||||||
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)
|
||||||
scrollView.refreshControl = refreshControl
|
scrollView.refreshControl = refreshControl
|
||||||
|
|
||||||
// Создаем хостинг для нашего SwiftUI контента
|
|
||||||
let hostingController = UIHostingController(rootView: content)
|
let hostingController = UIHostingController(rootView: content)
|
||||||
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
scrollView.addSubview(hostingController.view)
|
scrollView.addSubview(hostingController.view)
|
||||||
|
|
||||||
// Настраиваем Auto Layout
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
hostingController.view.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
|
hostingController.view.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
|
||||||
hostingController.view.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
|
hostingController.view.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
|
||||||
@ -40,14 +37,15 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateUIView(_ uiView: UIScrollView, context: Context) {
|
func updateUIView(_ uiView: UIScrollView, context: Context) {
|
||||||
// Обновляем состояние индикатора
|
|
||||||
if isRefreshing.wrappedValue {
|
if isRefreshing.wrappedValue {
|
||||||
uiView.refreshControl?.beginRefreshing()
|
uiView.refreshControl?.beginRefreshing()
|
||||||
} else {
|
} else {
|
||||||
|
// Отложенное завершение, чтобы избежать цикла обновлений
|
||||||
|
DispatchQueue.main.async {
|
||||||
uiView.refreshControl?.endRefreshing()
|
uiView.refreshControl?.endRefreshing()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Обновляем SwiftUI View, если нужно
|
|
||||||
context.coordinator.hostingController?.rootView = content
|
context.coordinator.hostingController?.rootView = content
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,9 +61,8 @@ struct RefreshableScrollView<Content: View>: UIViewRepresentable {
|
|||||||
self.parent = parent
|
self.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleRefresh(_ sender: UIRefreshControl) {
|
@objc func handleRefresh() {
|
||||||
parent.isRefreshing.wrappedValue = true
|
parent.onRefresh()
|
||||||
parent.onRefresh(sender)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ struct HomeTab: View {
|
|||||||
if isLoading {
|
if isLoading {
|
||||||
ProgressView("Загрузка ленты...")
|
ProgressView("Загрузка ленты...")
|
||||||
} else {
|
} else {
|
||||||
RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: { _ in
|
RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: {
|
||||||
fetchData()
|
fetchData()
|
||||||
}) {
|
}) {
|
||||||
LazyVStack(spacing: 24) {
|
LazyVStack(spacing: 24) {
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import SwiftUI
|
|||||||
|
|
||||||
struct ProfileContentTabbedGrid: View {
|
struct ProfileContentTabbedGrid: View {
|
||||||
let isContentLoaded: Bool
|
let isContentLoaded: Bool
|
||||||
|
@Binding var allPosts: [Post]
|
||||||
|
@Binding var isLoading: Bool
|
||||||
@State private var selectedTabIndex = 0
|
@State private var selectedTabIndex = 0
|
||||||
@State private var selectedCategory = "#все"
|
@State private var selectedCategory = "#все"
|
||||||
@State private var searchQuery = ""
|
@State private var searchQuery = ""
|
||||||
@State private var selectedSort = "По дате"
|
@State private var selectedSort = "По дате"
|
||||||
@State private var allPosts: [Post] = []
|
|
||||||
@State private var isLoading = true
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@ -73,15 +73,6 @@ struct ProfileContentTabbedGrid: View {
|
|||||||
isLoading: isLoading
|
isLoading: isLoading
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.onAppear {
|
|
||||||
if allPosts.isEmpty {
|
|
||||||
isLoading = true
|
|
||||||
PostService.shared.fetchAllPosts { result in
|
|
||||||
self.allPosts = result
|
|
||||||
self.isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Вкладка с меню
|
// MARK: - Вкладка с меню
|
||||||
|
|||||||
@ -2,13 +2,6 @@ import SwiftUI
|
|||||||
|
|
||||||
struct ProfileTab: View {
|
struct ProfileTab: View {
|
||||||
@ObservedObject var viewModel: LoginViewModel
|
@ObservedObject var viewModel: LoginViewModel
|
||||||
// @State private var selectedTabIndex = 0
|
|
||||||
// @State private var selectedSort = "По дате"
|
|
||||||
// @State private var selectedCategory = "#все"
|
|
||||||
// @State private var searchQuery = ""
|
|
||||||
// @State private var searchQueryArchive = ""
|
|
||||||
// @State private var searchQuerySaved = ""
|
|
||||||
// @State private var searchQueryLiked = ""
|
|
||||||
@State private var isContentLoaded = true
|
@State private var isContentLoaded = true
|
||||||
|
|
||||||
@State private var accounts = ["@user1", "@user2", "@user3"]
|
@State private var accounts = ["@user1", "@user2", "@user3"]
|
||||||
@ -20,43 +13,38 @@ struct ProfileTab: View {
|
|||||||
@State private var sheetType: SheetType? = nil
|
@State private var sheetType: SheetType? = nil
|
||||||
enum SheetType: Identifiable {
|
enum SheetType: Identifiable {
|
||||||
case accountShare
|
case accountShare
|
||||||
|
var id: Int { self.hashValue }
|
||||||
|
}
|
||||||
|
|
||||||
var id: Int {
|
@State private var allPosts: [Post] = []
|
||||||
switch self {
|
@State private var isLoading = true
|
||||||
case .accountShare: return 1
|
@State private var isRefreshing = false
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
if !isContentLoaded {
|
if !isContentLoaded {
|
||||||
SplashScreenView()
|
SplashScreenView()
|
||||||
} else {
|
} else {
|
||||||
GeometryReader { geometry in
|
RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: {
|
||||||
|
fetchData()
|
||||||
|
}) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ScrollView {
|
|
||||||
// ───── Шапка профиля (занимает ~50% экрана) ─────
|
|
||||||
header
|
header
|
||||||
.frame(minHeight: geometry.size.height * 0.5)
|
.frame(minHeight: 300)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.background(Color(.systemBackground))
|
.background(Color(.systemBackground))
|
||||||
|
|
||||||
// ───── Скролл с контентом ─────
|
ProfileContentTabbedGrid(
|
||||||
|
isContentLoaded: isContentLoaded,
|
||||||
ProfileContentTabbedGrid(isContentLoaded: isContentLoaded)
|
allPosts: $allPosts,
|
||||||
|
isLoading: $isLoading
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
||||||
}
|
}
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
Button(action: {
|
Button(action: { sheetType = .accountShare }) {
|
||||||
sheetType = .accountShare
|
|
||||||
}) {
|
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Text("custom_user_name")
|
Text("custom_user_name")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
@ -67,7 +55,6 @@ struct ProfileTab: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
NavigationLink(destination: SettingsView(viewModel: viewModel)) {
|
NavigationLink(destination: SettingsView(viewModel: viewModel)) {
|
||||||
Image(systemName: "wrench")
|
Image(systemName: "wrench")
|
||||||
@ -91,13 +78,28 @@ struct ProfileTab: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
// .onAppear {
|
.onAppear {
|
||||||
// if !isContentLoaded {
|
if allPosts.isEmpty {
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
fetchData(isInitialLoad: true)
|
||||||
// isContentLoaded = true
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
|
private func fetchData(isInitialLoad: Bool = false) {
|
||||||
|
if isInitialLoad {
|
||||||
|
isLoading = true
|
||||||
|
} else {
|
||||||
|
isRefreshing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
PostService.shared.fetchAllPosts { fetchedPosts in
|
||||||
|
self.allPosts = fetchedPosts
|
||||||
|
|
||||||
|
if isInitialLoad {
|
||||||
|
self.isLoading = false
|
||||||
|
}
|
||||||
|
self.isRefreshing = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Шапка профиля
|
// MARK: - Шапка профиля
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user