refresh profile

This commit is contained in:
cheykrym 2025-07-16 07:56:47 +03:00
parent 8fd955d3cb
commit a716b296d5
4 changed files with 91 additions and 101 deletions

View File

@ -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)
} }
} }
} }

View File

@ -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) {

View File

@ -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: - Вкладка с меню

View File

@ -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: - Шапка профиля